blob: 5bce4272f419b36527bec00af0ff681277ba288b [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
Rob Landley2d6af162006-06-21 00:52:31 +000024#include "busybox.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
Eric Andersen1c0d3112001-04-16 15:46:44 +000035#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +000036
37// Misc. non-Ascii keys that report an escape sequence
Denis Vlasenko2a51af22007-03-21 22:31:24 +000038#define VI_K_UP (char)128 // cursor key Up
39#define VI_K_DOWN (char)129 // cursor key Down
40#define VI_K_RIGHT (char)130 // Cursor Key Right
41#define VI_K_LEFT (char)131 // cursor key Left
42#define VI_K_HOME (char)132 // Cursor Key Home
43#define VI_K_END (char)133 // Cursor Key End
44#define VI_K_INSERT (char)134 // Cursor Key Insert
45#define VI_K_PAGEUP (char)135 // Cursor Key Page Up
46#define VI_K_PAGEDOWN (char)136 // Cursor Key Page Down
47#define VI_K_FUN1 (char)137 // Function Key F1
48#define VI_K_FUN2 (char)138 // Function Key F2
49#define VI_K_FUN3 (char)139 // Function Key F3
50#define VI_K_FUN4 (char)140 // Function Key F4
51#define VI_K_FUN5 (char)141 // Function Key F5
52#define VI_K_FUN6 (char)142 // Function Key F6
53#define VI_K_FUN7 (char)143 // Function Key F7
54#define VI_K_FUN8 (char)144 // Function Key F8
55#define VI_K_FUN9 (char)145 // Function Key F9
56#define VI_K_FUN10 (char)146 // Function Key F10
57#define VI_K_FUN11 (char)147 // Function Key F11
58#define VI_K_FUN12 (char)148 // Function Key F12
Eric Andersen3f980402001-04-04 17:31:15 +000059
Glenn L McGrath09adaca2002-12-02 21:18:10 +000060/* vt102 typical ESC sequence */
61/* terminal standout start/normal ESC sequence */
62static const char SOs[] = "\033[7m";
63static const char SOn[] = "\033[0m";
64/* terminal bell sequence */
65static const char bell[] = "\007";
66/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
67static const char Ceol[] = "\033[0K";
68static const char Ceos [] = "\033[0J";
69/* Cursor motion arbitrary destination ESC sequence */
70static const char CMrc[] = "\033[%d;%dH";
71/* Cursor motion up and down ESC sequence */
72static const char CMup[] = "\033[A";
73static const char CMdown[] = "\n";
74
75
Rob Landleybc68cd12006-03-10 19:22:06 +000076enum {
77 YANKONLY = FALSE,
78 YANKDEL = TRUE,
79 FORWARD = 1, // code depends on "1" for array index
80 BACK = -1, // code depends on "-1" for array index
81 LIMITED = 0, // how much of text[] in char_search
82 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +000083
Rob Landleybc68cd12006-03-10 19:22:06 +000084 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
85 S_TO_WS = 2, // used in skip_thing() for moving "dot"
86 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
87 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
Denis Vlasenko8e858e22007-03-07 09:35:43 +000088 S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
Rob Landleybc68cd12006-03-10 19:22:06 +000089};
Eric Andersen3f980402001-04-04 17:31:15 +000090
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +000091/* vi.c expects chars to be unsigned. */
92/* busybox build system provides that, but it's better */
93/* to audit and fix the source */
Eric Andersen3f980402001-04-04 17:31:15 +000094
Glenn L McGrath09adaca2002-12-02 21:18:10 +000095static int vi_setops;
96#define VI_AUTOINDENT 1
97#define VI_SHOWMATCH 2
98#define VI_IGNORECASE 4
99#define VI_ERR_METHOD 8
100#define autoindent (vi_setops & VI_AUTOINDENT)
101#define showmatch (vi_setops & VI_SHOWMATCH )
102#define ignorecase (vi_setops & VI_IGNORECASE)
103/* indicate error with beep or flash */
104#define err_method (vi_setops & VI_ERR_METHOD)
105
Eric Andersen3f980402001-04-04 17:31:15 +0000106
107static int editing; // >0 while we are editing a file
Paul Fox8552aec2005-09-16 12:20:05 +0000108static int cmd_mode; // 0=command 1=insert 2=replace
Eric Andersen3f980402001-04-04 17:31:15 +0000109static int file_modified; // buffer contents changed
Paul Fox8552aec2005-09-16 12:20:05 +0000110static int last_file_modified = -1;
Eric Andersen3f980402001-04-04 17:31:15 +0000111static int fn_start; // index of first cmd line file name
112static int save_argc; // how many file names on cmd line
113static int cmdcnt; // repetition count
114static fd_set rfds; // use select() for small sleeps
115static struct timeval tv; // use select() for small sleeps
Eric Andersen3f980402001-04-04 17:31:15 +0000116static int rows, columns; // the terminal screen is this size
117static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000118static char *status_buffer; // mesages to the user
Paul Fox8552aec2005-09-16 12:20:05 +0000119#define STATUS_BUFFER_LEN 200
120static int have_status_msg; // is default edit status needed?
121static int last_status_cksum; // hash of current status line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000122static char *cfn; // previous, current, and next file name
123static char *text, *end; // pointers to the user data in memory
124static char *screen; // pointer to the virtual screen buffer
Eric Andersen3f980402001-04-04 17:31:15 +0000125static int screensize; // and its size
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000126static char *screenbegin; // index into text[], of top line on the screen
127static char *dot; // where all the action takes place
Eric Andersen3f980402001-04-04 17:31:15 +0000128static int tabstop;
129static struct termios term_orig, term_vi; // remember what the cooked mode was
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000130static char erase_char; // the users erase character
131static char last_input_char; // last char read from user
132static char last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000133
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000134#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000135static int last_row; // where the cursor was last moved to
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000136#endif
137#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000138static jmp_buf restart; // catch_sig()
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000139#endif
140#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000141static int my_pid;
142#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000143#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000144static int adding2q; // are we currently adding user input to q
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000145static char *last_modifying_cmd; // last modifying cmd for "."
146static char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000147#endif
148#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000149static char *modifying_cmds; // cmds that modify text[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000150#endif
151#if ENABLE_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000152static int vi_readonly, readonly;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000153#endif
154#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000155static char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
Eric Andersen3f980402001-04-04 17:31:15 +0000156static int YDreg, Ureg; // default delete register and orig line for "U"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000157static char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
158static char *context_start, *context_end;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000159#endif
160#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000161static char *last_search_pattern; // last pattern from a '/' or '?' search
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000162#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000163
164
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000165static void edit_file(char *); // edit one file
166static void do_cmd(char); // execute a command
167static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
168static char *begin_line(char *); // return pointer to cur line B-o-l
169static char *end_line(char *); // return pointer to cur line E-o-l
170static char *prev_line(char *); // return pointer to prev line B-o-l
171static char *next_line(char *); // return pointer to next line B-o-l
172static char *end_screen(void); // get pointer to last char on screen
173static int count_lines(char *, char *); // count line from start to stop
174static char *find_line(int); // find begining of line #li
175static char *move_to_col(char *, int); // move "p" to column l
176static int isblnk(char); // is the char a blank or tab
Eric Andersen3f980402001-04-04 17:31:15 +0000177static void dot_left(void); // move dot left- dont leave line
178static void dot_right(void); // move dot right- dont leave line
179static void dot_begin(void); // move dot to B-o-l
180static void dot_end(void); // move dot to E-o-l
181static void dot_next(void); // move dot to next line B-o-l
182static void dot_prev(void); // move dot to prev line B-o-l
183static void dot_scroll(int, int); // move the screen up or down
184static void dot_skip_over_ws(void); // move dot pat WS
185static void dot_delete(void); // delete the char at 'dot'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000186static char *bound_dot(char *); // make sure text[0] <= P < "end"
187static char *new_screen(int, int); // malloc virtual screen memory
188static char *new_text(int); // malloc memory for text[] buffer
189static char *char_insert(char *, char); // insert the char c at 'p'
190static char *stupid_insert(char *, char); // stupidly insert the char c at 'p'
191static char find_range(char **, char **, char); // return pointers for an object
192static int st_test(char *, int, int, char *); // helper for skip_thing()
193static char *skip_thing(char *, int, int, int); // skip some object
194static char *find_pair(char *, char); // find matching pair () [] {}
195static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole
196static char *text_hole_make(char *, int); // at "p", make a 'size' byte hole
197static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
Eric Andersen3f980402001-04-04 17:31:15 +0000198static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000199static void rawmode(void); // set "raw" mode on tty
200static void cookmode(void); // return to "cooked" mode on tty
201static int mysleep(int); // sleep for 'h' 1/100 seconds
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000202static char readit(void); // read (maybe cursor) key from stdin
203static char get_one_char(void); // read 1 char from stdin
204static int file_size(const char *); // what is the byte size of "fn"
205static int file_insert(char *, char *, int);
206static int file_write(char *, char *, char *);
Eric Andersen822c3832001-05-07 17:37:43 +0000207static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000208static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000209static void clear_to_eol(void);
210static void clear_to_eos(void);
211static void standout_start(void); // send "start reverse video" sequence
212static void standout_end(void); // send "end reverse video" sequence
213static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000214static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000215static void psb(const char *, ...); // Print Status Buf
216static void psbs(const char *, ...); // Print Status Buf in standout mode
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000217static void ni(const char *); // display messages
Paul Fox8552aec2005-09-16 12:20:05 +0000218static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000219static void redraw(int); // force a full screen refresh
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000220static void format_line(char*, char*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000221static void refresh(int); // update the terminal from screen[]
222
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000223static void Indicate_Error(void); // use flash or beep to indicate error
224#define indicate_error(c) Indicate_Error()
Paul Fox90372ed2005-10-09 14:26:26 +0000225static void Hit_Return(void);
226
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000227#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000228static char *char_search(char *, const char *, int, int); // search for pattern starting at p
229static int mycmp(const char *, const char *, int); // string cmp based in "ignorecase"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000230#endif
231#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000232static char *get_one_address(char *, int *); // get colon addr, if present
233static char *get_address(char *, int *, int *); // get two colon addrs, if present
234static void colon(char *); // execute the "colon" mode cmds
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000235#endif
236#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000237static void winch_sig(int); // catch window size changes
238static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000239static void catch_sig(int); // catch ctrl-C and alarm time-outs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000240#endif
241#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000242static void start_new_cmd_q(char); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000243static void end_cmd_q(void); // stop saving input chars
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000244#else
245#define end_cmd_q() ((void)0)
246#endif
247#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000248static void showmatching(char *); // show the matching pair () [] {}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000249#endif
250#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000251static char *string_insert(char *, char *); // insert the string at 'p'
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000252#endif
253#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000254static char *text_yank(char *, char *, int); // save copy of "p" into a register
255static char what_reg(void); // what is letter of current YDreg
256static void check_context(char); // remember context for '' command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000257#endif
258#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000259static void crash_dummy();
260static void crash_test();
261static int crashme = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000262#endif
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000263#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000264static char *initial_cmds[] = { NULL, NULL , NULL }; // currently 2 entries, NULL terminated
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000265#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000266
267
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000268static void write1(const char *out)
269{
270 fputs(out, stdout);
271}
272
Denis Vlasenko06af2162007-02-03 17:28:39 +0000273int vi_main(int argc, char **argv);
Rob Landleydfba7412006-03-06 20:47:33 +0000274int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000275{
Eric Andersend402edf2001-04-04 19:29:48 +0000276 int c;
Paul Fox8552aec2005-09-16 12:20:05 +0000277 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
Eric Andersen3f980402001-04-04 17:31:15 +0000278
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000279#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000280 int i;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000281#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000282#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
283 my_pid = getpid();
284#endif
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000285#if ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000286 srand((long) my_pid);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000287#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000288
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000289 status_buffer = STATUS_BUFFER;
Paul Fox8552aec2005-09-16 12:20:05 +0000290 last_status_cksum = 0;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000291
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000292#if ENABLE_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000293 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000294 if (strncmp(argv[0], "view", 4) == 0) {
295 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000296 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000297 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000298#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000299 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000300#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000301 for (i = 0; i < 28; i++) {
302 reg[i] = 0;
303 } // init the yank regs
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000304#endif
305#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000306 modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000307#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000308
Denis Vlasenkof9234132007-03-21 00:03:42 +0000309 // 1- process $HOME/.exrc file (not inplemented yet)
Eric Andersen3f980402001-04-04 17:31:15 +0000310 // 2- process EXINIT variable from environment
311 // 3- process command line args
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000312#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000313 {
314 char *p = getenv("EXINIT");
315 if (p && *p)
316 initial_cmds[0] = xstrdup(p);
317 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000318#endif
319 while ((c = getopt(argc, argv, "hCR" USE_FEATURE_VI_COLON("c:"))) != -1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000320 switch (c) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000321#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000322 case 'C':
323 crashme = 1;
324 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000325#endif
326#if ENABLE_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000327 case 'R': // Read-only flag
328 readonly = TRUE;
Eric Andersen90fb65f2004-03-31 11:12:51 +0000329 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000330 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000331#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000332 //case 'r': // recover flag- ignore- we don't use tmp file
333 //case 'x': // encryption flag- ignore
334 //case 'c': // execute command first
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000335#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000336 case 'c': // cmd line vi command
337 if (*optarg)
338 initial_cmds[initial_cmds[0] != 0] = xstrdup(optarg);
339 break;
Eric Andersen822c3832001-05-07 17:37:43 +0000340 //case 'h': // help -- just use default
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000341#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000342 default:
343 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000344 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000345 }
346 }
347
348 // The argv array can be used by the ":next" and ":rewind" commands
349 // save optind.
350 fn_start = optind; // remember first file name for :next and :rew
351 save_argc = argc;
352
353 //----- This is the main file handling loop --------------
354 if (optind >= argc) {
355 editing = 1; // 0= exit, 1= one file, 2= multiple files
356 edit_file(0);
357 } else {
358 for (; optind < argc; optind++) {
359 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000360 free(cfn);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000361 cfn = xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000362 edit_file(cfn);
363 }
364 }
365 //-----------------------------------------------------------
366
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000367 return 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000368}
369
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000370static void edit_file(char * fn)
Eric Andersen3f980402001-04-04 17:31:15 +0000371{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000372 char c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000373 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000374
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000375#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000376 int sig;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000377#endif
378#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000379 static char *cur_line;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000380#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000381
382 rawmode();
383 rows = 24;
384 columns = 80;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000385 ch = -1;
Rob Landleye5e1a102006-06-21 01:15:36 +0000386 if (ENABLE_FEATURE_VI_WIN_RESIZE)
387 get_terminal_width_height(0, &columns, &rows);
Eric Andersen3f980402001-04-04 17:31:15 +0000388 new_screen(rows, columns); // get memory for virtual screen
389
390 cnt = file_size(fn); // file size
391 size = 2 * cnt; // 200% of file size
392 new_text(size); // get a text[] buffer
393 screenbegin = dot = end = text;
394 if (fn != 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000395 ch = file_insert(fn, text, cnt);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000396 }
397 if (ch < 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000398 char_insert(text, '\n'); // start empty buf with dummy line
Eric Andersen3f980402001-04-04 17:31:15 +0000399 }
Paul Fox8552aec2005-09-16 12:20:05 +0000400 file_modified = 0;
401 last_file_modified = -1;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000402#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000403 YDreg = 26; // default Yank/Delete reg
404 Ureg = 27; // hold orig line for "U" cmd
405 for (cnt = 0; cnt < 28; cnt++) {
406 mark[cnt] = 0;
407 } // init the marks
408 mark[26] = mark[27] = text; // init "previous context"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000409#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000410
Eric Andersen3f980402001-04-04 17:31:15 +0000411 last_forward_char = last_input_char = '\0';
412 crow = 0;
413 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000414
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000415#if ENABLE_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000416 catch_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000417 signal(SIGWINCH, winch_sig);
418 signal(SIGTSTP, suspend_sig);
419 sig = setjmp(restart);
420 if (sig != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000421 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000422 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000423#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000424
425 editing = 1;
426 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
427 cmdcnt = 0;
428 tabstop = 8;
429 offset = 0; // no horizontal offset
430 c = '\0';
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000431#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000432 free(last_modifying_cmd);
433 free(ioq_start);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000434 ioq = ioq_start = last_modifying_cmd = NULL;
Eric Andersen3f980402001-04-04 17:31:15 +0000435 adding2q = 0;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000436#endif
Eric Andersen822c3832001-05-07 17:37:43 +0000437 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000438 show_status_line();
439
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000440#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkof9234132007-03-21 00:03:42 +0000441 {
442 char *p, *q;
443 int n = 0;
444
445 while ((p = initial_cmds[n])) {
446 do {
447 q = p;
448 p = strchr(q,'\n');
449 if (p)
450 while(*p == '\n')
451 *p++ = '\0';
452 if (*q)
453 colon(q);
454 } while (p);
455 free(initial_cmds[n]);
456 initial_cmds[n] = NULL;
457 n++;
458 }
459 }
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000460#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000461 //------This is the main Vi cmd handling loop -----------------------
462 while (editing > 0) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000463#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000464 if (crashme > 0) {
465 if ((end - text) > 1) {
466 crash_dummy(); // generate a random command
467 } else {
468 crashme = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000469 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 +0000470 refresh(FALSE);
471 }
472 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000473#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000474 last_input_char = c = get_one_char(); // get a cmd from user
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000475#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000476 // save a copy of the current line- for the 'U" command
477 if (begin_line(dot) != cur_line) {
478 cur_line = begin_line(dot);
479 text_yank(begin_line(dot), end_line(dot), Ureg);
480 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000481#endif
482#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000483 // These are commands that change text[].
484 // Remember the input for the "." command
485 if (!adding2q && ioq_start == 0
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000486 && strchr(modifying_cmds, c)
487 ) {
Eric Andersen3f980402001-04-04 17:31:15 +0000488 start_new_cmd_q(c);
489 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000490#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000491 do_cmd(c); // execute the user command
492 //
493 // poll to see if there is input already waiting. if we are
494 // not able to display output fast enough to keep up, skip
495 // the display update until we catch up with input.
496 if (mysleep(0) == 0) {
497 // no input pending- so update output
498 refresh(FALSE);
499 show_status_line();
500 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000501#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000502 if (crashme > 0)
503 crash_test(); // test editor variables
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000504#endif
Eric Andersen3f980402001-04-04 17:31:15 +0000505 }
506 //-------------------------------------------------------------------
507
Eric Andersen822c3832001-05-07 17:37:43 +0000508 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000509 clear_to_eol(); // Erase to end of line
510 cookmode();
511}
512
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000513//----- The Colon commands -------------------------------------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000514#if ENABLE_FEATURE_VI_COLON
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000515static char *get_one_address(char * p, int *addr) // get colon addr, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000516{
517 int st;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000518 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000519
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000520#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000521 char c;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000522#endif
523#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000524 char *pat, buf[BUFSIZ];
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000525#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000526
527 *addr = -1; // assume no addr
528 if (*p == '.') { // the current line
529 p++;
530 q = begin_line(dot);
531 *addr = count_lines(text, q);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000532#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000533 } else if (*p == '\'') { // is this a mark addr
534 p++;
535 c = tolower(*p);
536 p++;
537 if (c >= 'a' && c <= 'z') {
538 // we have a mark
539 c = c - 'a';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000540 q = mark[(unsigned char) c];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000541 if (q != NULL) { // is mark valid
542 *addr = count_lines(text, q); // count lines
543 }
544 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000545#endif
546#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000547 } else if (*p == '/') { // a search pattern
548 q = buf;
549 for (p++; *p; p++) {
550 if (*p == '/')
551 break;
552 *q++ = *p;
553 *q = '\0';
554 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000555 pat = xstrdup(buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000556 if (*p == '/')
557 p++;
558 q = char_search(dot, pat, FORWARD, FULL);
559 if (q != NULL) {
560 *addr = count_lines(text, q);
561 }
562 free(pat);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000563#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000564 } else if (*p == '$') { // the last line in file
565 p++;
566 q = begin_line(end - 1);
567 *addr = count_lines(text, q);
568 } else if (isdigit(*p)) { // specific line number
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000569 sscanf(p, "%d%n", addr, &st);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000570 p += st;
571 } else { // I don't reconise this
572 // unrecognised address- assume -1
573 *addr = -1;
574 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000575 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000576}
577
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000578static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000579{
580 //----- get the address' i.e., 1,3 'a,'b -----
581 // get FIRST addr, if present
582 while (isblnk(*p))
583 p++; // skip over leading spaces
584 if (*p == '%') { // alias for 1,$
585 p++;
586 *b = 1;
587 *e = count_lines(text, end-1);
588 goto ga0;
589 }
590 p = get_one_address(p, b);
591 while (isblnk(*p))
592 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000593 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000594 p++;
595 while (isblnk(*p))
596 p++;
597 // get SECOND addr, if present
598 p = get_one_address(p, e);
599 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000600 ga0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000601 while (isblnk(*p))
602 p++; // skip over trailing spaces
Denis Vlasenko079f8af2006-11-27 16:49:31 +0000603 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000604}
605
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000606#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000607static void setops(const char *args, const char *opname, int flg_no,
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000608 const char *short_opname, int opt)
609{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000610 const char *a = args + flg_no;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000611 int l = strlen(opname) - 1; /* opname have + ' ' */
612
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000613 if (strncasecmp(a, opname, l) == 0
614 || strncasecmp(a, short_opname, 2) == 0
615 ) {
616 if (flg_no)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000617 vi_setops &= ~opt;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000618 else
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000619 vi_setops |= opt;
620 }
621}
622#endif
623
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000624static void colon(char * buf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000625{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000626 char c, *orig_buf, *buf1, *q, *r;
627 char *fn, cmd[BUFSIZ], args[BUFSIZ];
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000628 int i, l, li, ch, b, e;
Eric Andersena9eb33d2004-08-19 19:15:06 +0000629 int useforce = FALSE, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000630 struct stat st_buf;
631
632 // :3154 // if (-e line 3154) goto it else stay put
633 // :4,33w! foo // write a portion of buffer to file "foo"
634 // :w // write all of buffer to current file
635 // :q // quit
636 // :q! // quit- dont care about modified file
637 // :'a,'z!sort -u // filter block through sort
638 // :'f // goto mark "f"
639 // :'fl // list literal the mark "f" line
640 // :.r bar // read file "bar" into buffer before dot
641 // :/123/,/abc/d // delete lines from "123" line to "abc" line
642 // :/xyz/ // goto the "xyz" line
643 // :s/find/replace/ // substitute pattern "find" with "replace"
644 // :!<cmd> // run <cmd> then return
645 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000646
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000647 if (!buf[0])
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000648 goto vc1;
649 if (*buf == ':')
650 buf++; // move past the ':'
651
Bernhard Reutner-Fischerd591a362006-08-20 17:35:13 +0000652 li = ch = i = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000653 b = e = -1;
654 q = text; // assume 1,$ for the range
655 r = end - 1;
656 li = count_lines(text, end - 1);
657 fn = cfn; // default to current file
658 memset(cmd, '\0', BUFSIZ); // clear cmd[]
659 memset(args, '\0', BUFSIZ); // clear args[]
660
661 // look for optional address(es) :. :1 :1,9 :'q,'a :%
662 buf = get_address(buf, &b, &e);
663
664 // remember orig command line
665 orig_buf = buf;
666
667 // get the COMMAND into cmd[]
668 buf1 = cmd;
669 while (*buf != '\0') {
670 if (isspace(*buf))
671 break;
672 *buf1++ = *buf++;
673 }
674 // get any ARGuments
675 while (isblnk(*buf))
676 buf++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000677 strcpy(args, buf);
678 buf1 = last_char_is(cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000679 if (buf1) {
680 useforce = TRUE;
681 *buf1 = '\0'; // get rid of !
682 }
683 if (b >= 0) {
684 // if there is only one addr, then the addr
685 // is the line number of the single line the
686 // user wants. So, reset the end
687 // pointer to point at end of the "b" line
688 q = find_line(b); // what line is #b
689 r = end_line(q);
690 li = 1;
691 }
692 if (e >= 0) {
693 // we were given two addrs. change the
694 // end pointer to the addr given by user.
695 r = find_line(e); // what line is #e
696 r = end_line(r);
697 li = e - b + 1;
698 }
699 // ------------ now look for the command ------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000700 i = strlen(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000701 if (i == 0) { // :123CR goto line #123
702 if (b >= 0) {
703 dot = find_line(b); // what line is #b
704 dot_skip_over_ws();
705 }
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000706 }
707#if ENABLE_FEATURE_ALLOW_EXEC
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000708 else if (strncmp(cmd, "!", 1) == 0) { // run a cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000709 // :!ls run the <cmd>
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000710 alarm(0); // wait for input- no alarms
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000711 place_cursor(rows - 1, 0, FALSE); // go to Status line
712 clear_to_eol(); // clear the line
713 cookmode();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000714 system(orig_buf + 1); // run the cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000715 rawmode();
716 Hit_Return(); // let user see results
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000717 alarm(3); // done waiting for input
Denis Vlasenko249fabf2006-12-19 00:29:22 +0000718 }
719#endif
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000720 else if (strncmp(cmd, "=", i) == 0) { // where is the address
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000721 if (b < 0) { // no addr given- use defaults
722 b = e = count_lines(text, dot);
723 }
724 psb("%d", b);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000725 } else if (strncasecmp(cmd, "delete", i) == 0) { // delete lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000726 if (b < 0) { // no addr given- use defaults
727 q = begin_line(dot); // assume .,. for the range
728 r = end_line(dot);
729 }
730 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
731 dot_skip_over_ws();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000732 } else if (strncasecmp(cmd, "edit", i) == 0) { // Edit a file
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000733 int sr;
734 sr= 0;
735 // don't edit, if the current file has been modified
736 if (file_modified && ! useforce) {
737 psbs("No write since last change (:edit! overrides)");
738 goto vc1;
739 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000740 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000741 // the user supplied a file name
742 fn= args;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000743 } else if (cfn && cfn[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000744 // no user supplied name- use the current filename
745 fn= cfn;
746 goto vc5;
747 } else {
748 // no user file name, no current name- punt
749 psbs("No current filename");
750 goto vc1;
751 }
752
753 // see if file exists- if not, its just a new file request
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000754 sr = stat(fn, &st_buf);
755 if (sr < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000756 // This is just a request for a new file creation.
757 // The file_insert below will fail but we get
758 // an empty buffer with a file name. Then the "write"
759 // command can do the create.
760 } else {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000761 if ((st_buf.st_mode & S_IFREG) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000762 // This is not a regular file
763 psbs("\"%s\" is not a regular file", fn);
764 goto vc1;
765 }
766 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
767 // dont have any read permissions
768 psbs("\"%s\" is not readable", fn);
769 goto vc1;
770 }
771 }
772
773 // There is a read-able regular file
774 // make this the current file
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000775 q = xstrdup(fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000776 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000777 cfn = q; // remember new cfn
778
Denis Vlasenkobaca1752007-03-11 13:43:10 +0000779 vc5:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000780 // delete all the contents of text[]
781 new_text(2 * file_size(fn));
782 screenbegin = dot = end = text;
783
784 // insert new file
785 ch = file_insert(fn, text, file_size(fn));
786
787 if (ch < 1) {
788 // start empty buf with dummy line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000789 char_insert(text, '\n');
790 ch = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000791 }
Paul Fox8552aec2005-09-16 12:20:05 +0000792 file_modified = 0;
793 last_file_modified = -1;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000794#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000795 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
796 free(reg[Ureg]); // free orig line reg- for 'U'
797 reg[Ureg]= 0;
798 }
799 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
800 free(reg[YDreg]); // free default yank/delete register
801 reg[YDreg]= 0;
802 }
803 for (li = 0; li < 28; li++) {
804 mark[li] = 0;
805 } // init the marks
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000806#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000807 // how many lines in text[]?
808 li = count_lines(text, end - 1);
809 psb("\"%s\"%s"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000810#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000811 "%s"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000812#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000813 " %dL, %dC", cfn,
814 (sr < 0 ? " [New file]" : ""),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000815#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000816 ((vi_readonly || readonly) ? " [Read only]" : ""),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000817#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000818 li, ch);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000819 } else if (strncasecmp(cmd, "file", i) == 0) { // what File is this
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000820 if (b != -1 || e != -1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000821 ni("No address allowed on this command");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000822 goto vc1;
823 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000824 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000825 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000826 free(cfn);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000827 cfn = xstrdup(args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000828 } else {
829 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +0000830 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000831 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000832 } else if (strncasecmp(cmd, "features", i) == 0) { // what features are available
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000833 // print out values of all features
834 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
835 clear_to_eol(); // clear the line
836 cookmode();
837 show_help();
838 rawmode();
839 Hit_Return();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000840 } else if (strncasecmp(cmd, "list", i) == 0) { // literal print line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000841 if (b < 0) { // no addr given- use defaults
842 q = begin_line(dot); // assume .,. for the range
843 r = end_line(dot);
844 }
845 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
846 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000847 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000848 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000849 int c_is_no_print;
850
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000851 c = *q;
Denis Vlasenko2a51af22007-03-21 22:31:24 +0000852 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000853 if (c_is_no_print) {
854 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000855 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000856 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000857 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000858 write1("$\r");
859 } else if (c < ' ' || c == 127) {
860 putchar('^');
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000861 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000862 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000863 else
864 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000865 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000866 putchar(c);
867 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000868 standout_end();
869 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000870#if ENABLE_FEATURE_VI_SET
871 vc2:
872#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000873 Hit_Return();
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000874 } else if (strncasecmp(cmd, "quit", i) == 0 // Quit
875 || strncasecmp(cmd, "next", i) == 0 // edit next file
876 ) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000877 if (useforce) {
878 // force end of argv list
879 if (*cmd == 'q') {
880 optind = save_argc;
881 }
882 editing = 0;
883 goto vc1;
884 }
885 // don't exit if the file been modified
886 if (file_modified) {
887 psbs("No write since last change (:%s! overrides)",
888 (*cmd == 'q' ? "quit" : "next"));
889 goto vc1;
890 }
891 // are there other file to edit
892 if (*cmd == 'q' && optind < save_argc - 1) {
893 psbs("%d more file to edit", (save_argc - optind - 1));
894 goto vc1;
895 }
896 if (*cmd == 'n' && optind >= save_argc - 1) {
897 psbs("No more files to edit");
898 goto vc1;
899 }
900 editing = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000901 } else if (strncasecmp(cmd, "read", i) == 0) { // read file into text[]
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000902 fn = args;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000903 if (!fn[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000904 psbs("No filename given");
905 goto vc1;
906 }
907 if (b < 0) { // no addr given- use defaults
908 q = begin_line(dot); // assume "dot"
909 }
910 // read after current line- unless user said ":0r foo"
911 if (b != 0)
912 q = next_line(q);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000913#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000914 l = readonly; // remember current files' status
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000915#endif
916 ch = file_insert(fn, q, file_size(fn));
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000917#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000918 readonly= l;
919#endif
920 if (ch < 0)
921 goto vc1; // nothing was inserted
922 // how many lines in text[]?
923 li = count_lines(q, q + ch - 1);
924 psb("\"%s\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000925#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000926 "%s"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000927#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000928 " %dL, %dC", fn,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000929#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000930 ((vi_readonly || readonly) ? " [Read only]" : ""),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000931#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000932 li, ch);
933 if (ch > 0) {
934 // if the insert is before "dot" then we need to update
935 if (q <= dot)
936 dot += ch;
Paul Fox8552aec2005-09-16 12:20:05 +0000937 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000938 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000939 } else if (strncasecmp(cmd, "rewind", i) == 0) { // rewind cmd line args
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000940 if (file_modified && ! useforce) {
941 psbs("No write since last change (:rewind! overrides)");
942 } else {
943 // reset the filenames to edit
944 optind = fn_start - 1;
945 editing = 0;
946 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000947#if ENABLE_FEATURE_VI_SET
Denis Vlasenkof9234132007-03-21 00:03:42 +0000948 } else if (strncasecmp(cmd, "set", i) == 0) { // set or clear features
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000949#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkof9234132007-03-21 00:03:42 +0000950 char *argp;
Denis Vlasenko58875ae2007-03-22 22:22:10 +0000951#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000952 i = 0; // offset into args
Denis Vlasenkof9234132007-03-21 00:03:42 +0000953 // only blank is regarded as args delmiter. What about tab '\t' ?
954 if (!args[0] || strcasecmp(args, "all") == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000955 // print out values of all options
956 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
957 clear_to_eol(); // clear the line
958 printf("----------------------------------------\r\n");
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000959#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000960 if (!autoindent)
961 printf("no");
962 printf("autoindent ");
963 if (!err_method)
964 printf("no");
965 printf("flash ");
966 if (!ignorecase)
967 printf("no");
968 printf("ignorecase ");
969 if (!showmatch)
970 printf("no");
971 printf("showmatch ");
972 printf("tabstop=%d ", tabstop);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000973#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000974 printf("\r\n");
975 goto vc2;
976 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000977#if ENABLE_FEATURE_VI_SETOPTS
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000978 argp = args;
Denis Vlasenkoba2fb712007-04-01 09:39:03 +0000979 while (*argp) {
Denis Vlasenkof9234132007-03-21 00:03:42 +0000980 if (strncasecmp(argp, "no", 2) == 0)
981 i = 2; // ":set noautoindent"
982 setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
983 setops(argp, "flash ", i, "fl", VI_ERR_METHOD);
984 setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
985 setops(argp, "showmatch ", i, "ic", VI_SHOWMATCH);
986 /* tabstopXXXX */
987 if (strncasecmp(argp + i, "tabstop=%d ", 7) == 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +0000988 sscanf(strchr(argp + i, '='), "tabstop=%d" + 7, &ch);
Denis Vlasenkof9234132007-03-21 00:03:42 +0000989 if (ch > 0 && ch < columns - 1)
990 tabstop = ch;
991 }
992 while (*argp && *argp != ' ')
993 argp++; // skip to arg delimiter (i.e. blank)
994 while (*argp && *argp == ' ')
995 argp++; // skip all delimiting blanks
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000996 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +0000997#endif /* FEATURE_VI_SETOPTS */
998#endif /* FEATURE_VI_SET */
999#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001000 } else if (strncasecmp(cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1001 char *ls, *F, *R;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001002 int gflag;
1003
1004 // F points to the "find" pattern
1005 // R points to the "replace" pattern
1006 // replace the cmd line delimiters "/" with NULLs
1007 gflag = 0; // global replace flag
1008 c = orig_buf[1]; // what is the delimiter
1009 F = orig_buf + 2; // start of "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001010 R = strchr(F, c); // middle delimiter
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001011 if (!R) goto colon_s_fail;
1012 *R++ = '\0'; // terminate "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001013 buf1 = strchr(R, c);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001014 if (!buf1) goto colon_s_fail;
1015 *buf1++ = '\0'; // terminate "replace"
1016 if (*buf1 == 'g') { // :s/foo/bar/g
1017 buf1++;
1018 gflag++; // turn on gflag
1019 }
1020 q = begin_line(q);
1021 if (b < 0) { // maybe :s/foo/bar/
1022 q = begin_line(dot); // start with cur line
1023 b = count_lines(text, q); // cur line number
1024 }
1025 if (e < 0)
1026 e = b; // maybe :.s/foo/bar/
1027 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1028 ls = q; // orig line start
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001029 vc4:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001030 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001031 if (buf1) {
1032 // we found the "find" pattern - delete it
1033 text_hole_delete(buf1, buf1 + strlen(F) - 1);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001034 // inset the "replace" patern
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001035 string_insert(buf1, R); // insert the string
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001036 // check for "global" :s/foo/bar/g
1037 if (gflag == 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001038 if ((buf1 + strlen(R)) < end_line(ls)) {
1039 q = buf1 + strlen(R);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001040 goto vc4; // don't let q move past cur line
1041 }
1042 }
1043 }
1044 q = next_line(ls);
1045 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001046#endif /* FEATURE_VI_SEARCH */
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001047 } else if (strncasecmp(cmd, "version", i) == 0) { // show software version
Rob Landleyd921b2e2006-08-03 15:41:12 +00001048 psb("%s", BB_VER " " BB_BT);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001049 } else if (strncasecmp(cmd, "write", i) == 0 // write text to file
1050 || strncasecmp(cmd, "wq", i) == 0
1051 || strncasecmp(cmd, "wn", i) == 0
1052 || strncasecmp(cmd, "x", i) == 0
1053 ) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001054 // is there a file name to write to?
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001055 if (args[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001056 fn = args;
1057 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001058#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001059 if ((vi_readonly || readonly) && ! useforce) {
1060 psbs("\"%s\" File is read only", fn);
1061 goto vc3;
1062 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001063#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001064 // how many lines in text[]?
1065 li = count_lines(q, r);
1066 ch = r - q + 1;
1067 // see if file exists- if not, its just a new file request
1068 if (useforce) {
1069 // if "fn" is not write-able, chmod u+w
1070 // sprintf(syscmd, "chmod u+w %s", fn);
1071 // system(syscmd);
1072 forced = TRUE;
1073 }
1074 l = file_write(fn, q, r);
1075 if (useforce && forced) {
1076 // chmod u-w
1077 // sprintf(syscmd, "chmod u-w %s", fn);
1078 // system(syscmd);
1079 forced = FALSE;
1080 }
Paul Fox61e45db2005-10-09 14:43:22 +00001081 if (l < 0) {
1082 if (l == -1)
1083 psbs("Write error: %s", strerror(errno));
1084 } else {
1085 psb("\"%s\" %dL, %dC", fn, li, l);
1086 if (q == text && r == end - 1 && l == ch) {
1087 file_modified = 0;
1088 last_file_modified = -1;
1089 }
Paul Fox9360f422006-03-27 21:51:16 +00001090 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
1091 cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
1092 && l == ch) {
Paul Fox61e45db2005-10-09 14:43:22 +00001093 editing = 0;
1094 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001095 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001096#if ENABLE_FEATURE_VI_READONLY
1097 vc3:;
1098#endif
1099#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001100 } else if (strncasecmp(cmd, "yank", i) == 0) { // yank lines
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001101 if (b < 0) { // no addr given- use defaults
1102 q = begin_line(dot); // assume .,. for the range
1103 r = end_line(dot);
1104 }
1105 text_yank(q, r, YDreg);
1106 li = count_lines(q, r);
1107 psb("Yank %d lines (%d chars) into [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001108 li, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001109#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001110 } else {
1111 // cmd unknown
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001112 ni(cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001113 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001114 vc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001115 dot = bound_dot(dot); // make sure "dot" is valid
1116 return;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001117#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001118 colon_s_fail:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001119 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001120#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001121}
Paul Fox61e45db2005-10-09 14:43:22 +00001122
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001123#endif /* FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001124
1125static void Hit_Return(void)
1126{
1127 char c;
1128
1129 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001130 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001131 standout_end(); // end reverse video
1132 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1133 ;
1134 redraw(TRUE); // force redraw all
1135}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001136
1137//----- Synchronize the cursor to Dot --------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001138static void sync_cursor(char * d, int *row, int *col)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001139{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001140 char *beg_cur; // begin and end of "d" line
1141 char *end_scr; // begin and end of screen
1142 char *tp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001143 int cnt, ro, co;
1144
1145 beg_cur = begin_line(d); // first char of cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001146
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001147 end_scr = end_screen(); // last char of screen
1148
1149 if (beg_cur < screenbegin) {
1150 // "d" is before top line on screen
1151 // how many lines do we have to move
1152 cnt = count_lines(beg_cur, screenbegin);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001153 sc1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001154 screenbegin = beg_cur;
1155 if (cnt > (rows - 1) / 2) {
1156 // we moved too many lines. put "dot" in middle of screen
1157 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1158 screenbegin = prev_line(screenbegin);
1159 }
1160 }
1161 } else if (beg_cur > end_scr) {
1162 // "d" is after bottom line on screen
1163 // how many lines do we have to move
1164 cnt = count_lines(end_scr, beg_cur);
1165 if (cnt > (rows - 1) / 2)
1166 goto sc1; // too many lines
1167 for (ro = 0; ro < cnt - 1; ro++) {
1168 // move screen begin the same amount
1169 screenbegin = next_line(screenbegin);
1170 // now, move the end of screen
1171 end_scr = next_line(end_scr);
1172 end_scr = end_line(end_scr);
1173 }
1174 }
1175 // "d" is on screen- find out which row
1176 tp = screenbegin;
1177 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1178 if (tp == beg_cur)
1179 break;
1180 tp = next_line(tp);
1181 }
1182
1183 // find out what col "d" is on
1184 co = 0;
1185 do { // drive "co" to correct column
1186 if (*tp == '\n' || *tp == '\0')
1187 break;
1188 if (*tp == '\t') {
1189 // 7 - (co % 8 )
1190 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001191 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001192 co++; // display as ^X, use 2 columns
1193 }
1194 } while (tp++ < d && ++co);
1195
1196 // "co" is the column where "dot" is.
1197 // The screen has "columns" columns.
1198 // The currently displayed columns are 0+offset -- columns+ofset
1199 // |-------------------------------------------------------------|
1200 // ^ ^ ^
1201 // offset | |------- columns ----------------|
1202 //
1203 // If "co" is already in this range then we do not have to adjust offset
1204 // but, we do have to subtract the "offset" bias from "co".
1205 // If "co" is outside this range then we have to change "offset".
1206 // If the first char of a line is a tab the cursor will try to stay
1207 // in column 7, but we have to set offset to 0.
1208
1209 if (co < 0 + offset) {
1210 offset = co;
1211 }
1212 if (co >= columns + offset) {
1213 offset = co - columns + 1;
1214 }
1215 // if the first char of the line is a tab, and "dot" is sitting on it
1216 // force offset to 0.
1217 if (d == beg_cur && *d == '\t') {
1218 offset = 0;
1219 }
1220 co -= offset;
1221
1222 *row = ro;
1223 *col = co;
1224}
1225
1226//----- Text Movement Routines ---------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001227static char *begin_line(char * p) // return pointer to first char cur line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001228{
1229 while (p > text && p[-1] != '\n')
1230 p--; // go to cur line B-o-l
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001231 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001232}
1233
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001234static char *end_line(char * p) // return pointer to NL of cur line line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001235{
1236 while (p < end - 1 && *p != '\n')
1237 p++; // go to cur line E-o-l
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001238 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001239}
1240
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001241static inline char *dollar_line(char * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001242{
1243 while (p < end - 1 && *p != '\n')
1244 p++; // go to cur line E-o-l
1245 // Try to stay off of the Newline
1246 if (*p == '\n' && (p - begin_line(p)) > 0)
1247 p--;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001248 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001249}
1250
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001251static char *prev_line(char * p) // return pointer first char prev line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001252{
1253 p = begin_line(p); // goto begining of cur line
1254 if (p[-1] == '\n' && p > text)
1255 p--; // step to prev line
1256 p = begin_line(p); // goto begining of prev line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001257 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001258}
1259
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001260static char *next_line(char * p) // return pointer first char next line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001261{
1262 p = end_line(p);
1263 if (*p == '\n' && p < end - 1)
1264 p++; // step to next line
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001265 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001266}
1267
1268//----- Text Information Routines ------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001269static char *end_screen(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001270{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001271 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001272 int cnt;
1273
1274 // find new bottom line
1275 q = screenbegin;
1276 for (cnt = 0; cnt < rows - 2; cnt++)
1277 q = next_line(q);
1278 q = end_line(q);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001279 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001280}
1281
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001282static int count_lines(char * start, char * stop) // count line from start to stop
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001283{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001284 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001285 int cnt;
1286
1287 if (stop < start) { // start and stop are backwards- reverse them
1288 q = start;
1289 start = stop;
1290 stop = q;
1291 }
1292 cnt = 0;
1293 stop = end_line(stop); // get to end of this line
1294 for (q = start; q <= stop && q <= end - 1; q++) {
1295 if (*q == '\n')
1296 cnt++;
1297 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001298 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001299}
1300
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001301static char *find_line(int li) // find begining of line #li
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
1305 for (q = text; li > 1; li--) {
1306 q = next_line(q);
1307 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001308 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001309}
1310
1311//----- Dot Movement Routines ----------------------------------
1312static void dot_left(void)
1313{
1314 if (dot > text && dot[-1] != '\n')
1315 dot--;
1316}
1317
1318static void dot_right(void)
1319{
1320 if (dot < end - 1 && *dot != '\n')
1321 dot++;
1322}
1323
1324static void dot_begin(void)
1325{
1326 dot = begin_line(dot); // return pointer to first char cur line
1327}
1328
1329static void dot_end(void)
1330{
1331 dot = end_line(dot); // return pointer to last char cur line
1332}
1333
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001334static char *move_to_col(char * p, int l)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001335{
1336 int co;
1337
1338 p = begin_line(p);
1339 co = 0;
1340 do {
1341 if (*p == '\n' || *p == '\0')
1342 break;
1343 if (*p == '\t') {
1344 // 7 - (co % 8 )
1345 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001346 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001347 co++; // display as ^X, use 2 columns
1348 }
1349 } while (++co <= l && p++ < end);
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001350 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001351}
1352
1353static void dot_next(void)
1354{
1355 dot = next_line(dot);
1356}
1357
1358static void dot_prev(void)
1359{
1360 dot = prev_line(dot);
1361}
1362
1363static void dot_scroll(int cnt, int dir)
1364{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001365 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001366
1367 for (; cnt > 0; cnt--) {
1368 if (dir < 0) {
1369 // scroll Backwards
1370 // ctrl-Y scroll up one line
1371 screenbegin = prev_line(screenbegin);
1372 } else {
1373 // scroll Forwards
1374 // ctrl-E scroll down one line
1375 screenbegin = next_line(screenbegin);
1376 }
1377 }
1378 // make sure "dot" stays on the screen so we dont scroll off
1379 if (dot < screenbegin)
1380 dot = screenbegin;
1381 q = end_screen(); // find new bottom line
1382 if (dot > q)
1383 dot = begin_line(q); // is dot is below bottom line?
1384 dot_skip_over_ws();
1385}
1386
1387static void dot_skip_over_ws(void)
1388{
1389 // skip WS
1390 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1391 dot++;
1392}
1393
1394static void dot_delete(void) // delete the char at 'dot'
1395{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001396 text_hole_delete(dot, dot);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001397}
1398
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001399static char *bound_dot(char * p) // make sure text[0] <= P < "end"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001400{
1401 if (p >= end && end > text) {
1402 p = end - 1;
1403 indicate_error('1');
1404 }
1405 if (p < text) {
1406 p = text;
1407 indicate_error('2');
1408 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001409 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001410}
1411
1412//----- Helper Utility Routines --------------------------------
1413
1414//----------------------------------------------------------------
1415//----- Char Routines --------------------------------------------
1416/* Chars that are part of a word-
1417 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1418 * Chars that are Not part of a word (stoppers)
1419 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1420 * Chars that are WhiteSpace
1421 * TAB NEWLINE VT FF RETURN SPACE
1422 * DO NOT COUNT NEWLINE AS WHITESPACE
1423 */
1424
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001425static char *new_screen(int ro, int co)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001426{
1427 int li;
1428
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001429 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001430 screensize = ro * co + 8;
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001431 screen = xmalloc(screensize);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001432 // initialize the new screen. assume this will be a empty file.
1433 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001434 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001435 for (li = 1; li < ro - 1; li++) {
1436 screen[(li * co) + 0] = '~';
1437 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001438 return screen;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001439}
1440
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001441static char *new_text(int size)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001442{
1443 if (size < 10240)
1444 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001445 free(text);
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001446 text = xmalloc(size + 8);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001447 memset(text, '\0', size); // clear new text[]
1448 //text += 4; // leave some room for "oops"
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001449 return text;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001450}
1451
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001452#if ENABLE_FEATURE_VI_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001453static int mycmp(const char * s1, const char * s2, int len)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001454{
1455 int i;
1456
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001457 i = strncmp(s1, s2, len);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001458#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001459 if (ignorecase) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001460 i = strncasecmp(s1, s2, len);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001461 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001462#endif
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001463 return i;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001464}
1465
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001466// search for pattern starting at p
1467static char *char_search(char * p, const char * pat, int dir, int range)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001468{
1469#ifndef REGEX_SEARCH
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001470 char *start, *stop;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001471 int len;
1472
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001473 len = strlen(pat);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001474 if (dir == FORWARD) {
1475 stop = end - 1; // assume range is p - end-1
1476 if (range == LIMITED)
1477 stop = next_line(p); // range is to next line
1478 for (start = p; start < stop; start++) {
1479 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001480 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001481 }
1482 }
1483 } else if (dir == BACK) {
1484 stop = text; // assume range is text - p
1485 if (range == LIMITED)
1486 stop = prev_line(p); // range is to prev line
1487 for (start = p - len; start >= stop; start--) {
1488 if (mycmp(start, pat, len) == 0) {
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001489 return start;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001490 }
1491 }
1492 }
1493 // pattern not found
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001494 return NULL;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001495#else /* REGEX_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001496 char *q;
1497 struct re_pattern_buffer preg;
1498 int i;
1499 int size, range;
1500
1501 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1502 preg.translate = 0;
1503 preg.fastmap = 0;
1504 preg.buffer = 0;
1505 preg.allocated = 0;
1506
1507 // assume a LIMITED forward search
1508 q = next_line(p);
1509 q = end_line(q);
1510 q = end - 1;
1511 if (dir == BACK) {
1512 q = prev_line(p);
1513 q = text;
1514 }
1515 // count the number of chars to search over, forward or backward
1516 size = q - p;
1517 if (size < 0)
1518 size = p - q;
1519 // RANGE could be negative if we are searching backwards
1520 range = q - p;
1521
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001522 q = re_compile_pattern(pat, strlen(pat), &preg);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001523 if (q != 0) {
1524 // The pattern was not compiled
1525 psbs("bad search pattern: \"%s\": %s", pat, q);
1526 i = 0; // return p if pattern not compiled
1527 goto cs1;
1528 }
1529
1530 q = p;
1531 if (range < 0) {
1532 q = p - size;
1533 if (q < text)
1534 q = text;
1535 }
1536 // search for the compiled pattern, preg, in p[]
1537 // range < 0- search backward
1538 // range > 0- search forward
1539 // 0 < start < size
1540 // re_search() < 0 not found or error
1541 // re_search() > 0 index of found pattern
1542 // struct pattern char int int int struct reg
1543 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1544 i = re_search(&preg, q, size, 0, range, 0);
1545 if (i == -1) {
1546 p = 0;
1547 i = 0; // return NULL if pattern not found
1548 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001549 cs1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001550 if (dir == FORWARD) {
1551 p = p + i;
1552 } else {
1553 p = p - i;
1554 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001555 return p;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001556#endif /* REGEX_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001557}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001558#endif /* FEATURE_VI_SEARCH */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001559
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001560static char *char_insert(char * p, char c) // insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001561{
1562 if (c == 22) { // Is this an ctrl-V?
1563 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1564 p--; // backup onto ^
1565 refresh(FALSE); // show the ^
1566 c = get_one_char();
1567 *p = c;
1568 p++;
Paul Fox8552aec2005-09-16 12:20:05 +00001569 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001570 } else if (c == 27) { // Is this an ESC?
1571 cmd_mode = 0;
1572 cmdcnt = 0;
1573 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001574 last_status_cksum = 0; // force status update
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001575 if ((p[-1] != '\n') && (dot > text)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001576 p--;
1577 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001578 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001579 // 123456789
1580 if ((p[-1] != '\n') && (dot>text)) {
1581 p--;
1582 p = text_hole_delete(p, p); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001583 }
1584 } else {
1585 // insert a char into text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001586 char *sp; // "save p"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001587
1588 if (c == 13)
1589 c = '\n'; // translate \r to \n
1590 sp = p; // remember addr of insert
1591 p = stupid_insert(p, c); // insert the char
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001592#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001593 if (showmatch && strchr(")]}", *sp) != NULL) {
1594 showmatching(sp);
1595 }
1596 if (autoindent && c == '\n') { // auto indent the new line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001597 char *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001598
1599 q = prev_line(p); // use prev line as templet
1600 for (; isblnk(*q); q++) {
1601 p = stupid_insert(p, *q); // insert the char
1602 }
1603 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001604#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001605 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001606 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001607}
1608
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001609static char *stupid_insert(char * p, char c) // stupidly insert the char c at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001610{
1611 p = text_hole_make(p, 1);
1612 if (p != 0) {
1613 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001614 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001615 p++;
1616 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001617 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001618}
1619
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001620static char find_range(char ** start, char ** stop, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001621{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001622 char *save_dot, *p, *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001623 int cnt;
1624
1625 save_dot = dot;
1626 p = q = dot;
1627
1628 if (strchr("cdy><", c)) {
1629 // these cmds operate on whole lines
1630 p = q = begin_line(p);
1631 for (cnt = 1; cnt < cmdcnt; cnt++) {
1632 q = next_line(q);
1633 }
1634 q = end_line(q);
1635 } else if (strchr("^%$0bBeEft", c)) {
1636 // These cmds operate on char positions
1637 do_cmd(c); // execute movement cmd
1638 q = dot;
1639 } else if (strchr("wW", c)) {
1640 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001641 // if we are at the next word's first char
1642 // step back one char
1643 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001644 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001645 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1646 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1647 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001648 if (dot > text && *dot == '\n')
1649 dot--; // stay off NL
1650 q = dot;
1651 } else if (strchr("H-k{", c)) {
1652 // these operate on multi-lines backwards
1653 q = end_line(dot); // find NL
1654 do_cmd(c); // execute movement cmd
1655 dot_begin();
1656 p = dot;
1657 } else if (strchr("L+j}\r\n", c)) {
1658 // these operate on multi-lines forwards
1659 p = begin_line(dot);
1660 do_cmd(c); // execute movement cmd
1661 dot_end(); // find NL
1662 q = dot;
1663 } else {
1664 c = 27; // error- return an ESC char
1665 //break;
1666 }
1667 *start = p;
1668 *stop = q;
1669 if (q < p) {
1670 *start = q;
1671 *stop = p;
1672 }
1673 dot = save_dot;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001674 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001675}
1676
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001677static int st_test(char * p, int type, int dir, char * tested)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001678{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001679 char c, c0, ci;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001680 int test, inc;
1681
1682 inc = dir;
1683 c = c0 = p[0];
1684 ci = p[inc];
1685 test = 0;
1686
1687 if (type == S_BEFORE_WS) {
1688 c = ci;
1689 test = ((!isspace(c)) || c == '\n');
1690 }
1691 if (type == S_TO_WS) {
1692 c = c0;
1693 test = ((!isspace(c)) || c == '\n');
1694 }
1695 if (type == S_OVER_WS) {
1696 c = c0;
1697 test = ((isspace(c)));
1698 }
1699 if (type == S_END_PUNCT) {
1700 c = ci;
1701 test = ((ispunct(c)));
1702 }
1703 if (type == S_END_ALNUM) {
1704 c = ci;
1705 test = ((isalnum(c)) || c == '_');
1706 }
1707 *tested = c;
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001708 return test;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001709}
1710
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001711static char *skip_thing(char * p, int linecnt, int dir, int type)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001712{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001713 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001714
1715 while (st_test(p, type, dir, &c)) {
1716 // make sure we limit search to correct number of lines
1717 if (c == '\n' && --linecnt < 1)
1718 break;
1719 if (dir >= 0 && p >= end - 1)
1720 break;
1721 if (dir < 0 && p <= text)
1722 break;
1723 p += dir; // move to next char
1724 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001725 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001726}
1727
1728// find matching char of pair () [] {}
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001729static char *find_pair(char * p, char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001730{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001731 char match, *q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001732 int dir, level;
1733
1734 match = ')';
1735 level = 1;
1736 dir = 1; // assume forward
1737 switch (c) {
1738 case '(':
1739 match = ')';
1740 break;
1741 case '[':
1742 match = ']';
1743 break;
1744 case '{':
1745 match = '}';
1746 break;
1747 case ')':
1748 match = '(';
1749 dir = -1;
1750 break;
1751 case ']':
1752 match = '[';
1753 dir = -1;
1754 break;
1755 case '}':
1756 match = '{';
1757 dir = -1;
1758 break;
1759 }
1760 for (q = p + dir; text <= q && q < end; q += dir) {
1761 // look for match, count levels of pairs (( ))
1762 if (*q == c)
1763 level++; // increase pair levels
1764 if (*q == match)
1765 level--; // reduce pair level
1766 if (level == 0)
1767 break; // found matching pair
1768 }
1769 if (level != 0)
1770 q = NULL; // indicate no match
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001771 return q;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001772}
1773
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001774#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001775// show the matching char of a pair, () [] {}
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001776static void showmatching(char * p)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001777{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001778 char *q, *save_dot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001779
1780 // we found half of a pair
1781 q = find_pair(p, *p); // get loc of matching char
1782 if (q == NULL) {
1783 indicate_error('3'); // no matching char
1784 } else {
1785 // "q" now points to matching pair
1786 save_dot = dot; // remember where we are
1787 dot = q; // go to new loc
1788 refresh(FALSE); // let the user see it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001789 mysleep(40); // give user some time
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001790 dot = save_dot; // go back to old loc
1791 refresh(FALSE);
1792 }
1793}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001794#endif /* FEATURE_VI_SETOPTS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001795
1796// open a hole in text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001797static char *text_hole_make(char * p, int size) // at "p", make a 'size' byte hole
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001798{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001799 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001800 int cnt;
1801
1802 if (size <= 0)
1803 goto thm0;
1804 src = p;
1805 dest = p + size;
1806 cnt = end - src; // the rest of buffer
1807 if (memmove(dest, src, cnt) != dest) {
1808 psbs("can't create room for new characters");
1809 }
1810 memset(p, ' ', size); // clear new hole
1811 end = end + size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001812 file_modified++; // has the file been modified
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001813 thm0:
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001814 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001815}
1816
1817// close a hole in text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001818static char *text_hole_delete(char * p, char * q) // delete "p" thru "q", inclusive
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001819{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001820 char *src, *dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001821 int cnt, hole_size;
1822
1823 // move forwards, from beginning
1824 // assume p <= q
1825 src = q + 1;
1826 dest = p;
1827 if (q < p) { // they are backward- swap them
1828 src = p + 1;
1829 dest = q;
1830 }
1831 hole_size = q - p + 1;
1832 cnt = end - src;
1833 if (src < text || src > end)
1834 goto thd0;
1835 if (dest < text || dest >= end)
1836 goto thd0;
1837 if (src >= end)
1838 goto thd_atend; // just delete the end of the buffer
1839 if (memmove(dest, src, cnt) != dest) {
1840 psbs("can't delete the character");
1841 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001842 thd_atend:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001843 end = end - hole_size; // adjust the new END
1844 if (dest >= end)
1845 dest = end - 1; // make sure dest in below end-1
1846 if (end <= text)
1847 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001848 file_modified++; // has the file been modified
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001849 thd0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00001850 return dest;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001851}
1852
1853// copy text into register, then delete text.
1854// if dist <= 0, do not include, or go past, a NewLine
1855//
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001856static char *yank_delete(char * start, char * stop, int dist, int yf)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001857{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001858 char *p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001859
1860 // make sure start <= stop
1861 if (start > stop) {
1862 // they are backwards, reverse them
1863 p = start;
1864 start = stop;
1865 stop = p;
1866 }
1867 if (dist <= 0) {
Denis Vlasenkoe1a0d482006-10-20 13:28:22 +00001868 // we cannot cross NL boundaries
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001869 p = start;
1870 if (*p == '\n')
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001871 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001872 // dont go past a NewLine
1873 for (; p + 1 <= stop; p++) {
1874 if (p[1] == '\n') {
1875 stop = p; // "stop" just before NewLine
1876 break;
1877 }
1878 }
1879 }
1880 p = start;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001881#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001882 text_yank(start, stop, YDreg);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001883#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001884 if (yf == YANKDEL) {
1885 p = text_hole_delete(start, stop);
1886 } // delete lines
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001887 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001888}
1889
1890static void show_help(void)
1891{
1892 puts("These features are available:"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001893#if ENABLE_FEATURE_VI_SEARCH
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001894 "\n\tPattern searches with / and ?"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001895#endif
1896#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001897 "\n\tLast command repeat with \'.\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001898#endif
1899#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001900 "\n\tLine marking with 'x"
1901 "\n\tNamed buffers with \"x"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001902#endif
1903#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001904 "\n\tReadonly if vi is called as \"view\""
1905 "\n\tReadonly with -R command line arg"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001906#endif
1907#if ENABLE_FEATURE_VI_SET
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001908 "\n\tSome colon mode commands with \':\'"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001909#endif
1910#if ENABLE_FEATURE_VI_SETOPTS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001911 "\n\tSettable options with \":set\""
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001912#endif
1913#if ENABLE_FEATURE_VI_USE_SIGNALS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001914 "\n\tSignal catching- ^C"
1915 "\n\tJob suspend and resume with ^Z"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001916#endif
1917#if ENABLE_FEATURE_VI_WIN_RESIZE
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001918 "\n\tAdapt to window re-sizes"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001919#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001920 );
1921}
1922
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001923static inline void print_literal(char * buf, const char * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001924{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001925 unsigned char c;
1926 char b[2];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001927
1928 b[1] = '\0';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001929 buf[0] = '\0';
1930 if (!s[0])
1931 s = "(NULL)";
1932 for (; *s; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001933 int c_is_no_print;
1934
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001935 c = *s;
Denis Vlasenko2a51af22007-03-21 22:31:24 +00001936 c_is_no_print = (c & 0x80) && !Isprint(c);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001937 if (c_is_no_print) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001938 strcat(buf, SOn);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001939 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001940 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001941 if (c < ' ' || c == 127) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001942 strcat(buf, "^");
1943 if (c == 127)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001944 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001945 else
1946 c += '@';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001947 }
1948 b[0] = c;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001949 strcat(buf, b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001950 if (c_is_no_print)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001951 strcat(buf, SOs);
1952 if (*s == '\n')
1953 strcat(buf, "$");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001954 }
1955}
1956
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001957#if ENABLE_FEATURE_VI_DOT_CMD
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001958static void start_new_cmd_q(char c)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001959{
1960 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001961 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001962 // get buffer for new cmd
Denis Vlasenkob95636c2006-12-19 23:36:04 +00001963 last_modifying_cmd = xmalloc(BUFSIZ);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001964 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1965 // if there is a current cmd count put it in the buffer first
1966 if (cmdcnt > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001967 sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
Paul Foxd957b952005-11-28 18:07:53 +00001968 else // just save char c onto queue
1969 last_modifying_cmd[0] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001970 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001971}
1972
1973static void end_cmd_q(void)
1974{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001975#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001976 YDreg = 26; // go back to default Yank/Delete reg
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001977#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001978 adding2q = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001979}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001980#endif /* FEATURE_VI_DOT_CMD */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001981
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001982#if ENABLE_FEATURE_VI_YANKMARK \
1983 || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
1984 || ENABLE_FEATURE_VI_CRASHME
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001985static char *string_insert(char * p, char * s) // insert the string at 'p'
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001986{
1987 int cnt, i;
1988
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001989 i = strlen(s);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001990 p = text_hole_make(p, i);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00001991 strncpy(p, s, i);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001992 for (cnt = 0; *s != '\0'; s++) {
1993 if (*s == '\n')
1994 cnt++;
1995 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001996#if ENABLE_FEATURE_VI_YANKMARK
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001997 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00001998#endif
Denis Vlasenko079f8af2006-11-27 16:49:31 +00001999 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002000}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002001#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002002
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002003#if ENABLE_FEATURE_VI_YANKMARK
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002004static char *text_yank(char * p, char * q, int dest) // copy text into a register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002005{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002006 char *t;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002007 int cnt;
2008
2009 if (q < p) { // they are backwards- reverse them
2010 t = q;
2011 q = p;
2012 p = t;
2013 }
2014 cnt = q - p + 1;
2015 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002016 free(t); // if already a yank register, free it
Denis Vlasenkob95636c2006-12-19 23:36:04 +00002017 t = xmalloc(cnt + 1); // get a new register
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002018 memset(t, '\0', cnt + 1); // clear new text[]
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002019 strncpy(t, p, cnt); // copy text[] into bufer
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002020 reg[dest] = t;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002021 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002022}
2023
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002024static char what_reg(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002025{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002026 char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002027
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002028 c = 'D'; // default to D-reg
2029 if (0 <= YDreg && YDreg <= 25)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002030 c = 'a' + (char) YDreg;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002031 if (YDreg == 26)
2032 c = 'D';
2033 if (YDreg == 27)
2034 c = 'U';
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002035 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002036}
2037
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002038static void check_context(char cmd)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002039{
2040 // A context is defined to be "modifying text"
2041 // Any modifying command establishes a new context.
2042
2043 if (dot < context_start || dot > context_end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002044 if (strchr(modifying_cmds, cmd) != NULL) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002045 // we are trying to modify text[]- make this the current context
2046 mark[27] = mark[26]; // move cur to prev
2047 mark[26] = dot; // move local to cur
2048 context_start = prev_line(prev_line(dot));
2049 context_end = next_line(next_line(dot));
2050 //loiter= start_loiter= now;
2051 }
2052 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002053}
2054
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002055static inline char *swap_context(char * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002056{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002057 char *tmp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002058
2059 // the current context is in mark[26]
2060 // the previous context is in mark[27]
2061 // only swap context if other context is valid
2062 if (text <= mark[27] && mark[27] <= end - 1) {
2063 tmp = mark[27];
2064 mark[27] = mark[26];
2065 mark[26] = tmp;
2066 p = mark[26]; // where we are going- previous context
2067 context_start = prev_line(prev_line(prev_line(p)));
2068 context_end = next_line(next_line(next_line(p)));
2069 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002070 return p;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002071}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002072#endif /* FEATURE_VI_YANKMARK */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002073
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002074static int isblnk(char c) // is the char a blank or tab
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002075{
2076 return (c == ' ' || c == '\t');
2077}
2078
2079//----- Set terminal attributes --------------------------------
2080static void rawmode(void)
2081{
2082 tcgetattr(0, &term_orig);
2083 term_vi = term_orig;
2084 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2085 term_vi.c_iflag &= (~IXON & ~ICRNL);
2086 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002087 term_vi.c_cc[VMIN] = 1;
2088 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002089 erase_char = term_vi.c_cc[VERASE];
2090 tcsetattr(0, TCSANOW, &term_vi);
2091}
2092
2093static void cookmode(void)
2094{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002095 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002096 tcsetattr(0, TCSANOW, &term_orig);
2097}
2098
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002099//----- Come here when we get a window resize signal ---------
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002100#if ENABLE_FEATURE_VI_USE_SIGNALS
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002101static void winch_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002102{
2103 signal(SIGWINCH, winch_sig);
Rob Landleye5e1a102006-06-21 01:15:36 +00002104 if (ENABLE_FEATURE_VI_WIN_RESIZE)
Denis Vlasenko621204b2006-10-27 09:03:24 +00002105 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002106 new_screen(rows, columns); // get memory for virtual screen
2107 redraw(TRUE); // re-draw the screen
2108}
2109
2110//----- Come here when we get a continue signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002111static void cont_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002112{
2113 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002114 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002115 redraw(TRUE); // re-draw the screen
2116
2117 signal(SIGTSTP, suspend_sig);
2118 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002119 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002120}
2121
2122//----- Come here when we get a Suspend signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002123static void suspend_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002124{
2125 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2126 clear_to_eol(); // Erase to end of line
2127 cookmode(); // terminal to "cooked"
2128
2129 signal(SIGCONT, cont_sig);
2130 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002131 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002132}
2133
2134//----- Come here when we get a signal ---------------------------
2135static void catch_sig(int sig)
2136{
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002137 signal(SIGINT, catch_sig);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002138 if (sig)
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002139 longjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002140}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002141#endif /* FEATURE_VI_USE_SIGNALS */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002142
2143static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2144{
2145 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002146 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002147 FD_ZERO(&rfds);
2148 FD_SET(0, &rfds);
2149 tv.tv_sec = 0;
2150 tv.tv_usec = hund * 10000;
2151 select(1, &rfds, NULL, NULL, &tv);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002152 return FD_ISSET(0, &rfds);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002153}
2154
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002155#define readbuffer bb_common_bufsiz1
2156
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002157static int readed_for_parse;
2158
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002159//----- IO Routines --------------------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002160static char readit(void) // read (maybe cursor) key from stdin
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002161{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002162 char c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002163 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002164 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002165 const char *seq;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002166 char val;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002167 };
2168
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002169 static const struct esc_cmds esccmds[] = {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002170 {"OA", VI_K_UP}, // cursor key Up
2171 {"OB", VI_K_DOWN}, // cursor key Down
2172 {"OC", VI_K_RIGHT}, // Cursor Key Right
2173 {"OD", VI_K_LEFT}, // cursor key Left
2174 {"OH", VI_K_HOME}, // Cursor Key Home
2175 {"OF", VI_K_END}, // Cursor Key End
2176 {"[A", VI_K_UP}, // cursor key Up
2177 {"[B", VI_K_DOWN}, // cursor key Down
2178 {"[C", VI_K_RIGHT}, // Cursor Key Right
2179 {"[D", VI_K_LEFT}, // cursor key Left
2180 {"[H", VI_K_HOME}, // Cursor Key Home
2181 {"[F", VI_K_END}, // Cursor Key End
2182 {"[1~", VI_K_HOME}, // Cursor Key Home
2183 {"[2~", VI_K_INSERT}, // Cursor Key Insert
2184 {"[4~", VI_K_END}, // Cursor Key End
2185 {"[5~", VI_K_PAGEUP}, // Cursor Key Page Up
2186 {"[6~", VI_K_PAGEDOWN},// Cursor Key Page Down
2187 {"OP", VI_K_FUN1}, // Function Key F1
2188 {"OQ", VI_K_FUN2}, // Function Key F2
2189 {"OR", VI_K_FUN3}, // Function Key F3
2190 {"OS", VI_K_FUN4}, // Function Key F4
2191 {"[15~", VI_K_FUN5}, // Function Key F5
2192 {"[17~", VI_K_FUN6}, // Function Key F6
2193 {"[18~", VI_K_FUN7}, // Function Key F7
2194 {"[19~", VI_K_FUN8}, // Function Key F8
2195 {"[20~", VI_K_FUN9}, // Function Key F9
2196 {"[21~", VI_K_FUN10}, // Function Key F10
2197 {"[23~", VI_K_FUN11}, // Function Key F11
2198 {"[24~", VI_K_FUN12}, // Function Key F12
2199 {"[11~", VI_K_FUN1}, // Function Key F1
2200 {"[12~", VI_K_FUN2}, // Function Key F2
2201 {"[13~", VI_K_FUN3}, // Function Key F3
2202 {"[14~", VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002203 };
2204
2205#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2206
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002207 alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002208 fflush(stdout);
2209 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002210 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002211 if (n <= 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002212 ri0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002213 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002214 n = read(0, readbuffer, BUFSIZ - 1);
2215 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002216 if (errno == EINTR)
2217 goto ri0; // interrupted sys call
2218 if (errno == EBADF)
2219 editing = 0;
2220 if (errno == EFAULT)
2221 editing = 0;
2222 if (errno == EINVAL)
2223 editing = 0;
2224 if (errno == EIO)
2225 editing = 0;
2226 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002227 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002228 if (n <= 0)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002229 return 0; // error
2230 if (readbuffer[0] == 27) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002231 // This is an ESC char. Is this Esc sequence?
2232 // Could be bare Esc key. See if there are any
2233 // more chars to read after the ESC. This would
2234 // be a Function or Cursor Key sequence.
2235 FD_ZERO(&rfds);
2236 FD_SET(0, &rfds);
2237 tv.tv_sec = 0;
2238 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002239
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002240 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002241 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002242 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002243 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2244 if (r > 0) {
2245 n += r;
2246 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002247 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002248 }
2249 readed_for_parse = n;
2250 }
2251 c = readbuffer[0];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002252 if (c == 27 && n > 1) {
2253 // Maybe cursor or function key?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002254 const struct esc_cmds *eindex;
2255
2256 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2257 int cnt = strlen(eindex->seq);
2258
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002259 if (n <= cnt)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002260 continue;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002261 if (strncmp(eindex->seq, readbuffer + 1, cnt))
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002262 continue;
2263 // is a Cursor key- put derived value back into Q
2264 c = eindex->val;
2265 // for squeeze out the ESC sequence
2266 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002267 break;
2268 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002269 if (eindex == &esccmds[ESCCMDS_COUNT]) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002270 /* defined ESC sequence not found, set only one ESC */
2271 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002272 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002273 } else {
2274 n = 1;
2275 }
2276 // remove key sequence from Q
2277 readed_for_parse -= n;
2278 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002279 alarm(3); // we are done waiting for input, turn alarm ON
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002280 return c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002281}
2282
2283//----- IO Routines --------------------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002284static char get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002285{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002286 static char c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002287
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002288#if ENABLE_FEATURE_VI_DOT_CMD
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002289 // ! adding2q && ioq == 0 read()
2290 // ! adding2q && ioq != 0 *ioq
2291 // adding2q *last_modifying_cmd= read()
2292 if (!adding2q) {
2293 // we are not adding to the q.
2294 // but, we may be reading from a q
2295 if (ioq == 0) {
2296 // there is no current q, read from STDIN
2297 c = readit(); // get the users input
2298 } else {
2299 // there is a queue to get chars from first
2300 c = *ioq++;
2301 if (c == '\0') {
2302 // the end of the q, read from STDIN
2303 free(ioq_start);
2304 ioq_start = ioq = 0;
2305 c = readit(); // get the users input
2306 }
2307 }
2308 } else {
2309 // adding STDIN chars to q
2310 c = readit(); // get the users input
2311 if (last_modifying_cmd != 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002312 int len = strlen(last_modifying_cmd);
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002313 if (len + 1 >= BUFSIZ) {
2314 psbs("last_modifying_cmd overrun");
2315 } else {
2316 // add new char to q
2317 last_modifying_cmd[len] = c;
2318 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002319 }
2320 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002321#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002322 c = readit(); // get the users input
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002323#endif /* FEATURE_VI_DOT_CMD */
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002324 return c; // return the char, where ever it came from
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002325}
2326
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002327static char *get_input_line(const char * prompt) // get input line- use "status line"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002328{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002329 static char *obufp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002330
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002331 char buf[BUFSIZ];
2332 char c;
2333 int i;
2334
2335 strcpy(buf, prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002336 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002337 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2338 clear_to_eol(); // clear the line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002339 write1(prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002340
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002341 i = strlen(buf);
2342 while (i < BUFSIZ) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002343 c = get_one_char(); // read user input
2344 if (c == '\n' || c == '\r' || c == 27)
2345 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002346 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002347 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002348 i--; // backup to prev char
2349 buf[i] = '\0'; // erase the char
2350 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002351 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002352 if (i <= 0) { // user backs up before b-o-l, exit
2353 break;
2354 }
2355 } else {
2356 buf[i] = c; // save char in buffer
2357 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002358 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002359 i++;
2360 }
2361 }
2362 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002363 free(obufp);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002364 obufp = xstrdup(buf);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002365 return obufp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002366}
2367
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002368static int file_size(const char * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002369{
2370 struct stat st_buf;
2371 int cnt, sr;
2372
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002373 if (!fn || !fn[0])
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002374 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002375 cnt = -1;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002376 sr = stat(fn, &st_buf); // see if file exists
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002377 if (sr >= 0) {
2378 cnt = (int) st_buf.st_size;
2379 }
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002380 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002381}
2382
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002383static int file_insert(char * fn, char * p, int size)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002384{
2385 int fd, cnt;
2386
2387 cnt = -1;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002388#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002389 readonly = FALSE;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002390#endif
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002391 if (!fn || !fn[0]) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002392 psbs("No filename given");
2393 goto fi0;
2394 }
2395 if (size == 0) {
2396 // OK- this is just a no-op
2397 cnt = 0;
2398 goto fi0;
2399 }
2400 if (size < 0) {
2401 psbs("Trying to insert a negative number (%d) of characters", size);
2402 goto fi0;
2403 }
2404 if (p < text || p > end) {
2405 psbs("Trying to insert file outside of memory");
2406 goto fi0;
2407 }
2408
2409 // see if we can open the file
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002410#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002411 if (vi_readonly) goto fi1; // do not try write-mode
2412#endif
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002413 fd = open(fn, O_RDWR); // assume read & write
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002414 if (fd < 0) {
2415 // could not open for writing- maybe file is read only
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002416#if ENABLE_FEATURE_VI_READONLY
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002417 fi1:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002418#endif
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002419 fd = open(fn, O_RDONLY); // try read-only
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002420 if (fd < 0) {
Denis Vlasenkoea620772006-10-14 02:23:43 +00002421 psbs("\"%s\" %s", fn, "cannot open file");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002422 goto fi0;
2423 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002424#if ENABLE_FEATURE_VI_READONLY
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002425 // got the file- read-only
2426 readonly = TRUE;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002427#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002428 }
2429 p = text_hole_make(p, size);
2430 cnt = read(fd, p, size);
2431 close(fd);
2432 if (cnt < 0) {
2433 cnt = -1;
2434 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
Denis Vlasenkoea620772006-10-14 02:23:43 +00002435 psbs("cannot read file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002436 } else if (cnt < size) {
2437 // There was a partial read, shrink unused space text[]
2438 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
Denis Vlasenkoea620772006-10-14 02:23:43 +00002439 psbs("cannot read all of file \"%s\"", fn);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002440 }
2441 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002442 file_modified++;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002443 fi0:
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002444 return cnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002445}
2446
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002447static int file_write(char * fn, char * first, char * last)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002448{
2449 int fd, cnt, charcnt;
2450
2451 if (fn == 0) {
2452 psbs("No current filename");
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002453 return -2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002454 }
2455 charcnt = 0;
2456 // FIXIT- use the correct umask()
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002457 fd = open(fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002458 if (fd < 0)
Denis Vlasenko079f8af2006-11-27 16:49:31 +00002459 return -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002460 cnt = last - first + 1;
2461 charcnt = write(fd, first, cnt);
2462 if (charcnt == cnt) {
2463 // good write
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002464 //file_modified = FALSE; // the file has not been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002465 } else {
2466 charcnt = 0;
2467 }
2468 close(fd);
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00002469 return charcnt;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002470}
2471
2472//----- Terminal Drawing ---------------------------------------
2473// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002474// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002475// screen coordinates
2476// 0,0 ... 0,79
2477// 1,0 ... 1,79
2478// . ... .
2479// . ... .
2480// 22,0 ... 22,79
2481// 23,0 ... 23,79 status line
2482//
2483
2484//----- Move the cursor to row x col (count from 0, not 1) -------
2485static void place_cursor(int row, int col, int opti)
2486{
2487 char cm1[BUFSIZ];
2488 char *cm;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002489#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002490 char cm2[BUFSIZ];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002491 char *screenp;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002492 // char cm3[BUFSIZ];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002493 int Rrow = last_row;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002494#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002495
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002496 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2497
2498 if (row < 0) row = 0;
2499 if (row >= rows) row = rows - 1;
2500 if (col < 0) col = 0;
2501 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002502
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002503 //----- 1. Try the standard terminal ESC sequence
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002504 sprintf(cm1, CMrc, row + 1, col + 1);
2505 cm = cm1;
2506 if (!opti)
2507 goto pc0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002508
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002509#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002510 //----- find the minimum # of chars to move cursor -------------
2511 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2512 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002513
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002514 // move to the correct row
2515 while (row < Rrow) {
2516 // the cursor has to move up
2517 strcat(cm2, CMup);
2518 Rrow--;
2519 }
2520 while (row > Rrow) {
2521 // the cursor has to move down
2522 strcat(cm2, CMdown);
2523 Rrow++;
2524 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002525
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002526 // now move to the correct column
2527 strcat(cm2, "\r"); // start at col 0
2528 // just send out orignal source char to get to correct place
2529 screenp = &screen[row * columns]; // start of screen line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002530 strncat(cm2, screenp, col);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002531
2532 //----- 3. Try some other way of moving cursor
2533 //---------------------------------------------
2534
2535 // pick the shortest cursor motion to send out
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002536 cm = cm1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002537 if (strlen(cm2) < strlen(cm)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002538 cm = cm2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002539 } /* else if (strlen(cm3) < strlen(cm)) {
2540 cm= cm3;
2541 } */
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002542#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
2543 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002544 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002545}
2546
2547//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002548static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002549{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002550 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002551}
2552
2553//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002554static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002555{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002556 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002557}
2558
2559//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002560static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002561{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002562 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002563}
2564
2565//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002566static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002567{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002568 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002569}
2570
2571//----- Flash the screen --------------------------------------
2572static void flash(int h)
2573{
2574 standout_start(); // send "start reverse video" sequence
2575 redraw(TRUE);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002576 mysleep(h);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002577 standout_end(); // send "end reverse video" sequence
2578 redraw(TRUE);
2579}
2580
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002581static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002582{
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002583#if ENABLE_FEATURE_VI_CRASHME
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002584 if (crashme > 0)
2585 return; // generate a random command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002586#endif
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002587 if (!err_method) {
2588 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002589 } else {
2590 flash(10);
2591 }
2592}
2593
2594//----- Screen[] Routines --------------------------------------
2595//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002596static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002597{
2598 memset(screen, ' ', screensize); // clear new screen
2599}
2600
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002601static int bufsum(char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002602{
2603 int sum = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002604 char *e = buf + count;
2605
Paul Fox8552aec2005-09-16 12:20:05 +00002606 while (buf < e)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002607 sum += (unsigned char) *buf++;
Paul Fox8552aec2005-09-16 12:20:05 +00002608 return sum;
2609}
2610
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002611//----- Draw the status line at bottom of the screen -------------
2612static void show_status_line(void)
2613{
Paul Foxc3504852005-09-16 12:48:18 +00002614 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002615
Paul Fox8552aec2005-09-16 12:20:05 +00002616 // either we already have an error or status message, or we
2617 // create one.
2618 if (!have_status_msg) {
2619 cnt = format_edit_status();
2620 cksum = bufsum(status_buffer, cnt);
2621 }
2622 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2623 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002624 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002625 write1(status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002626 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002627 if (have_status_msg) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002628 if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002629 (columns - 1) ) {
2630 have_status_msg = 0;
2631 Hit_Return();
2632 }
2633 have_status_msg = 0;
2634 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002635 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2636 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002637 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002638}
2639
2640//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002641// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002642static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002643{
2644 va_list args;
2645
2646 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002647 strcpy(status_buffer, SOs); // Terminal standout mode on
2648 vsprintf(status_buffer + strlen(status_buffer), format, args);
2649 strcat(status_buffer, SOn); // Terminal standout mode off
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002650 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002651
2652 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002653}
2654
Paul Fox8552aec2005-09-16 12:20:05 +00002655// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002656static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002657{
2658 va_list args;
2659
2660 va_start(args, format);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002661 vsprintf(status_buffer, format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002662 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002663
2664 have_status_msg = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002665}
2666
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002667static void ni(const char * s) // display messages
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002668{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002669 char buf[BUFSIZ];
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002670
2671 print_literal(buf, s);
2672 psbs("\'%s\' is not implemented", buf);
2673}
2674
Paul Fox8552aec2005-09-16 12:20:05 +00002675static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002676{
Paul Fox8552aec2005-09-16 12:20:05 +00002677 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002678
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002679 int cur, percent, ret, trunc_at;
2680
Paul Fox8552aec2005-09-16 12:20:05 +00002681 // file_modified is now a counter rather than a flag. this
2682 // helps reduce the amount of line counting we need to do.
2683 // (this will cause a mis-reporting of modified status
2684 // once every MAXINT editing operations.)
2685
2686 // it would be nice to do a similar optimization here -- if
2687 // we haven't done a motion that could have changed which line
2688 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002689 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002690
2691 // reduce counting -- the total lines can't have
2692 // changed if we haven't done any edits.
2693 if (file_modified != last_file_modified) {
2694 tot = cur + count_lines(dot, end - 1) - 1;
2695 last_file_modified = file_modified;
2696 }
2697
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002698 // current line percent
2699 // ------------- ~~ ----------
2700 // total lines 100
2701 if (tot > 0) {
2702 percent = (100 * cur) / tot;
2703 } else {
2704 cur = tot = 0;
2705 percent = 100;
2706 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002707
Paul Fox8552aec2005-09-16 12:20:05 +00002708 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2709 columns : STATUS_BUFFER_LEN-1;
2710
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002711 ret = snprintf(status_buffer, trunc_at+1,
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002712#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002713 "%c %s%s%s %d/%d %d%%",
2714#else
2715 "%c %s%s %d/%d %d%%",
2716#endif
2717 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002718 (cfn != 0 ? cfn : "No file"),
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002719#if ENABLE_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002720 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2721#endif
2722 (file_modified ? " [modified]" : ""),
2723 cur, tot, percent);
2724
2725 if (ret >= 0 && ret < trunc_at)
2726 return ret; /* it all fit */
2727
2728 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002729}
2730
2731//----- Force refresh of all Lines -----------------------------
2732static void redraw(int full_screen)
2733{
2734 place_cursor(0, 0, FALSE); // put cursor in correct place
2735 clear_to_eos(); // tel terminal to erase display
2736 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002737 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002738 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002739 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002740}
2741
2742//----- Format a text[] line into a buffer ---------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002743static void format_line(char *dest, char *src, int li)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002744{
2745 int co;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002746 char c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002747
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002748 for (co = 0; co < MAX_SCR_COLS; co++) {
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002749 c = ' '; // assume blank
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002750 if (li > 0 && co == 0) {
2751 c = '~'; // not first line, assume Tilde
2752 }
2753 // are there chars in text[] and have we gone past the end
2754 if (text < end && src < end) {
2755 c = *src++;
2756 }
2757 if (c == '\n')
2758 break;
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002759 if ((c & 0x80) && !Isprint(c)) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002760 c = '.';
2761 }
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002762 if ((unsigned char)(c) < ' ' || c == 0x7f) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002763 if (c == '\t') {
2764 c = ' ';
2765 // co % 8 != 7
2766 for (; (co % tabstop) != (tabstop - 1); co++) {
2767 dest[co] = c;
2768 }
2769 } else {
2770 dest[co++] = '^';
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002771 if (c == 0x7f)
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002772 c = '?';
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002773 else
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002774 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002775 }
2776 }
2777 // the co++ is done here so that the column will
2778 // not be overwritten when we blank-out the rest of line
2779 dest[co] = c;
2780 if (src >= end)
2781 break;
2782 }
2783}
2784
2785//----- Refresh the changed screen lines -----------------------
2786// Copy the source line from text[] into the buffer and note
2787// if the current screenline is different from the new buffer.
2788// If they differ then that line needs redrawing on the terminal.
2789//
2790static void refresh(int full_screen)
2791{
2792 static int old_offset;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002793
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002794 int li, changed;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002795 char buf[MAX_SCR_COLS];
2796 char *tp, *sp; // pointer into text[] and screen[]
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002797#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002798 int last_li = -2; // last line that changed- for optimizing cursor movement
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002799#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002800
Rob Landleye5e1a102006-06-21 01:15:36 +00002801 if (ENABLE_FEATURE_VI_WIN_RESIZE)
2802 get_terminal_width_height(0, &columns, &rows);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002803 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2804 tp = screenbegin; // index into text[] of top line
2805
2806 // compare text[] to screen[] and mark screen[] lines that need updating
2807 for (li = 0; li < rows - 1; li++) {
2808 int cs, ce; // column start & end
2809 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2810 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2811 // format current text line into buf
2812 format_line(buf, tp, li);
2813
2814 // skip to the end of the current text[] line
2815 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2816
2817 // see if there are any changes between vitual screen and buf
2818 changed = FALSE; // assume no change
2819 cs= 0;
2820 ce= columns-1;
2821 sp = &screen[li * columns]; // start of screen line
2822 if (full_screen) {
2823 // force re-draw of every single column from 0 - columns-1
2824 goto re0;
2825 }
2826 // compare newly formatted buffer with virtual screen
2827 // look forward for first difference between buf and screen
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002828 for (; cs <= ce; cs++) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002829 if (buf[cs + offset] != sp[cs]) {
2830 changed = TRUE; // mark for redraw
2831 break;
2832 }
2833 }
2834
2835 // look backward for last difference between buf and screen
2836 for ( ; ce >= cs; ce--) {
2837 if (buf[ce + offset] != sp[ce]) {
2838 changed = TRUE; // mark for redraw
2839 break;
2840 }
2841 }
2842 // now, cs is index of first diff, and ce is index of last diff
2843
2844 // if horz offset has changed, force a redraw
2845 if (offset != old_offset) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002846 re0:
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002847 changed = TRUE;
2848 }
2849
2850 // make a sanity check of columns indexes
2851 if (cs < 0) cs= 0;
2852 if (ce > columns-1) ce= columns-1;
2853 if (cs > ce) { cs= 0; ce= columns-1; }
2854 // is there a change between vitual screen and buf
2855 if (changed) {
2856 // copy changed part of buffer to virtual screen
2857 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2858
2859 // move cursor to column of first change
2860 if (offset != old_offset) {
2861 // opti_cur_move is still too stupid
2862 // to handle offsets correctly
2863 place_cursor(li, cs, FALSE);
2864 } else {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002865#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002866 // if this just the next line
2867 // try to optimize cursor movement
2868 // otherwise, use standard ESC sequence
2869 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2870 last_li= li;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002871#else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002872 place_cursor(li, cs, FALSE); // use standard ESC sequence
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002873#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002874 }
2875
2876 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002877 {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002878 int nic = ce - cs + 1;
2879 char *out = sp + cs;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002880
Denis Vlasenkobf0a2012006-12-26 10:42:51 +00002881 while (nic-- > 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002882 putchar(*out);
2883 out++;
2884 }
2885 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002886#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002887 last_row = li;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002888#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002889 }
2890 }
2891
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002892#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002893 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2894 last_row = crow;
2895#else
2896 place_cursor(crow, ccol, FALSE);
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002897#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002898
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002899 if (offset != old_offset)
2900 old_offset = offset;
2901}
2902
Eric Andersen3f980402001-04-04 17:31:15 +00002903//---------------------------------------------------------------------
2904//----- the Ascii Chart -----------------------------------------------
2905//
2906// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2907// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2908// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2909// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2910// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2911// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2912// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2913// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2914// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2915// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2916// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2917// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2918// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2919// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2920// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2921// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2922//---------------------------------------------------------------------
2923
2924//----- Execute a Vi Command -----------------------------------
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002925static void do_cmd(char c)
Eric Andersen3f980402001-04-04 17:31:15 +00002926{
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002927 const char *msg;
2928 char c1, *p, *q, buf[9], *save_dot;
Eric Andersen3f980402001-04-04 17:31:15 +00002929 int cnt, i, j, dir, yf;
2930
2931 c1 = c; // quiet the compiler
2932 cnt = yf = dir = 0; // quiet the compiler
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002933 msg = p = q = save_dot = buf; // quiet the compiler
Eric Andersen3f980402001-04-04 17:31:15 +00002934 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002935
Paul Fox8552aec2005-09-16 12:20:05 +00002936 show_status_line();
2937
Eric Andersenbff7a602001-11-17 07:15:43 +00002938 /* if this is a cursor key, skip these checks */
2939 switch (c) {
2940 case VI_K_UP:
2941 case VI_K_DOWN:
2942 case VI_K_LEFT:
2943 case VI_K_RIGHT:
2944 case VI_K_HOME:
2945 case VI_K_END:
2946 case VI_K_PAGEUP:
2947 case VI_K_PAGEDOWN:
2948 goto key_cmd_mode;
2949 }
2950
Eric Andersen3f980402001-04-04 17:31:15 +00002951 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002952 // flip-flop Insert/Replace mode
Denis Vlasenko2a51af22007-03-21 22:31:24 +00002953 if (c == VI_K_INSERT)
2954 goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00002955 // we are 'R'eplacing the current *dot with new char
2956 if (*dot == '\n') {
2957 // don't Replace past E-o-l
2958 cmd_mode = 1; // convert to insert
2959 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002960 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00002961 if (c != 27)
2962 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2963 dot = char_insert(dot, c); // insert new char
2964 }
2965 goto dc1;
2966 }
2967 }
2968 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00002969 // hitting "Insert" twice means "R" replace mode
2970 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00002971 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002972 if (1 <= c || Isprint(c)) {
2973 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00002974 }
2975 goto dc1;
2976 }
2977
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00002978 key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00002979 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00002980 //case 0x01: // soh
2981 //case 0x09: // ht
2982 //case 0x0b: // vt
2983 //case 0x0e: // so
2984 //case 0x0f: // si
2985 //case 0x10: // dle
2986 //case 0x11: // dc1
2987 //case 0x13: // dc3
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002988#if ENABLE_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00002989 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00002990 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00002991 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00002992#endif
Eric Andersen822c3832001-05-07 17:37:43 +00002993 //case 0x16: // syn
2994 //case 0x17: // etb
2995 //case 0x18: // can
2996 //case 0x1c: // fs
2997 //case 0x1d: // gs
2998 //case 0x1e: // rs
2999 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003000 //case '!': // !-
3001 //case '#': // #-
3002 //case '&': // &-
3003 //case '(': // (-
3004 //case ')': // )-
3005 //case '*': // *-
3006 //case ',': // ,-
3007 //case '=': // =-
3008 //case '@': // @-
3009 //case 'F': // F-
3010 //case 'K': // K-
3011 //case 'Q': // Q-
3012 //case 'S': // S-
3013 //case 'T': // T-
3014 //case 'V': // V-
3015 //case '[': // [-
3016 //case '\\': // \-
3017 //case ']': // ]-
3018 //case '_': // _-
3019 //case '`': // `-
3020 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003021 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003022 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003023 default: // unrecognised command
3024 buf[0] = c;
3025 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003026 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003027 buf[0] = '^';
3028 buf[1] = c + '@';
3029 buf[2] = '\0';
3030 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003031 ni(buf);
Eric Andersen3f980402001-04-04 17:31:15 +00003032 end_cmd_q(); // stop adding to q
3033 case 0x00: // nul- ignore
3034 break;
3035 case 2: // ctrl-B scroll up full screen
3036 case VI_K_PAGEUP: // Cursor Key Page Up
3037 dot_scroll(rows - 2, -1);
3038 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003039#if ENABLE_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003040 case 0x03: // ctrl-C interrupt
3041 longjmp(restart, 1);
3042 break;
3043 case 26: // ctrl-Z suspend
3044 suspend_sig(SIGTSTP);
3045 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003046#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003047 case 4: // ctrl-D scroll down half screen
3048 dot_scroll((rows - 2) / 2, 1);
3049 break;
3050 case 5: // ctrl-E scroll down one line
3051 dot_scroll(1, 1);
3052 break;
3053 case 6: // ctrl-F scroll down full screen
3054 case VI_K_PAGEDOWN: // Cursor Key Page Down
3055 dot_scroll(rows - 2, 1);
3056 break;
3057 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003058 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003059 break;
3060 case 'h': // h- move left
3061 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003062 case 8: // ctrl-H- move left (This may be ERASE char)
Denis Vlasenko2a51af22007-03-21 22:31:24 +00003063 case 0x7f: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003064 if (cmdcnt-- > 1) {
3065 do_cmd(c);
3066 } // repeat cnt
3067 dot_left();
3068 break;
3069 case 10: // Newline ^J
3070 case 'j': // j- goto next line, same col
3071 case VI_K_DOWN: // cursor key Down
3072 if (cmdcnt-- > 1) {
3073 do_cmd(c);
3074 } // repeat cnt
3075 dot_next(); // go to next B-o-l
3076 dot = move_to_col(dot, ccol + offset); // try stay in same col
3077 break;
3078 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003079 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003080 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003081 clear_to_eos(); // tel terminal to erase display
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003082 mysleep(10);
Eric Andersen3f980402001-04-04 17:31:15 +00003083 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003084 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003085 refresh(TRUE); // this will redraw the entire display
3086 break;
3087 case 13: // Carriage Return ^M
3088 case '+': // +- goto next line
3089 if (cmdcnt-- > 1) {
3090 do_cmd(c);
3091 } // repeat cnt
3092 dot_next();
3093 dot_skip_over_ws();
3094 break;
3095 case 21: // ctrl-U scroll up half screen
3096 dot_scroll((rows - 2) / 2, -1);
3097 break;
3098 case 25: // ctrl-Y scroll up one line
3099 dot_scroll(1, -1);
3100 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003101 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003102 if (cmd_mode == 0)
3103 indicate_error(c);
3104 cmd_mode = 0; // stop insrting
3105 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003106 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003107 break;
3108 case ' ': // move right
3109 case 'l': // move right
3110 case VI_K_RIGHT: // Cursor Key Right
3111 if (cmdcnt-- > 1) {
3112 do_cmd(c);
3113 } // repeat cnt
3114 dot_right();
3115 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003116#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003117 case '"': // "- name a register to use for Delete/Yank
3118 c1 = get_one_char();
3119 c1 = tolower(c1);
3120 if (islower(c1)) {
3121 YDreg = c1 - 'a';
3122 } else {
3123 indicate_error(c);
3124 }
3125 break;
3126 case '\'': // '- goto a specific mark
3127 c1 = get_one_char();
3128 c1 = tolower(c1);
3129 if (islower(c1)) {
3130 c1 = c1 - 'a';
3131 // get the b-o-l
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003132 q = mark[(unsigned char) c1];
Eric Andersen3f980402001-04-04 17:31:15 +00003133 if (text <= q && q < end) {
3134 dot = q;
3135 dot_begin(); // go to B-o-l
3136 dot_skip_over_ws();
3137 }
3138 } else if (c1 == '\'') { // goto previous context
3139 dot = swap_context(dot); // swap current and previous context
3140 dot_begin(); // go to B-o-l
3141 dot_skip_over_ws();
3142 } else {
3143 indicate_error(c);
3144 }
3145 break;
3146 case 'm': // m- Mark a line
3147 // this is really stupid. If there are any inserts or deletes
3148 // between text[0] and dot then this mark will not point to the
3149 // correct location! It could be off by many lines!
3150 // Well..., at least its quick and dirty.
3151 c1 = get_one_char();
3152 c1 = tolower(c1);
3153 if (islower(c1)) {
3154 c1 = c1 - 'a';
3155 // remember the line
3156 mark[(int) c1] = dot;
3157 } else {
3158 indicate_error(c);
3159 }
3160 break;
3161 case 'P': // P- Put register before
3162 case 'p': // p- put register after
3163 p = reg[YDreg];
3164 if (p == 0) {
3165 psbs("Nothing in register %c", what_reg());
3166 break;
3167 }
3168 // are we putting whole lines or strings
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003169 if (strchr(p, '\n') != NULL) {
Eric Andersen3f980402001-04-04 17:31:15 +00003170 if (c == 'P') {
3171 dot_begin(); // putting lines- Put above
3172 }
3173 if (c == 'p') {
3174 // are we putting after very last line?
3175 if (end_line(dot) == (end - 1)) {
3176 dot = end; // force dot to end of text[]
3177 } else {
3178 dot_next(); // next line, then put before
3179 }
3180 }
3181 } else {
3182 if (c == 'p')
3183 dot_right(); // move to right, can move to NL
3184 }
3185 dot = string_insert(dot, p); // insert the string
3186 end_cmd_q(); // stop adding to q
3187 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003188 case 'U': // U- Undo; replace current line with original version
3189 if (reg[Ureg] != 0) {
3190 p = begin_line(dot);
3191 q = end_line(dot);
3192 p = text_hole_delete(p, q); // delete cur line
3193 p = string_insert(p, reg[Ureg]); // insert orig line
3194 dot = p;
3195 dot_skip_over_ws();
3196 }
3197 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003198#endif /* FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003199 case '$': // $- goto end of line
3200 case VI_K_END: // Cursor Key End
3201 if (cmdcnt-- > 1) {
3202 do_cmd(c);
3203 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003204 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003205 break;
3206 case '%': // %- find matching char of pair () [] {}
3207 for (q = dot; q < end && *q != '\n'; q++) {
3208 if (strchr("()[]{}", *q) != NULL) {
3209 // we found half of a pair
3210 p = find_pair(q, *q);
3211 if (p == NULL) {
3212 indicate_error(c);
3213 } else {
3214 dot = p;
3215 }
3216 break;
3217 }
3218 }
3219 if (*q == '\n')
3220 indicate_error(c);
3221 break;
3222 case 'f': // f- forward to a user specified char
3223 last_forward_char = get_one_char(); // get the search char
3224 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003225 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003226 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003227 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003228 case ';': // ;- look at rest of line for last forward char
3229 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003230 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003231 } // repeat cnt
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003232 if (last_forward_char == 0)
3233 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003234 q = dot + 1;
3235 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3236 q++;
3237 }
3238 if (*q == last_forward_char)
3239 dot = q;
3240 break;
3241 case '-': // -- goto prev line
3242 if (cmdcnt-- > 1) {
3243 do_cmd(c);
3244 } // repeat cnt
3245 dot_prev();
3246 dot_skip_over_ws();
3247 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003248#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003249 case '.': // .- repeat the last modifying command
3250 // Stuff the last_modifying_cmd back into stdin
3251 // and let it be re-executed.
3252 if (last_modifying_cmd != 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003253 ioq = ioq_start = xstrdup(last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003254 }
3255 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003256#endif
3257#if ENABLE_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003258 case '?': // /- search for a pattern
3259 case '/': // /- search for a pattern
3260 buf[0] = c;
3261 buf[1] = '\0';
3262 q = get_input_line(buf); // get input line- use "status line"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003263 if (q[0] && !q[1])
3264 goto dc3; // if no pat re-use old pat
3265 if (q[0]) { // strlen(q) > 1: new pat- save it and find
Eric Andersen3f980402001-04-04 17:31:15 +00003266 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003267 free(last_search_pattern);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003268 last_search_pattern = xstrdup(q);
Eric Andersen3f980402001-04-04 17:31:15 +00003269 goto dc3; // now find the pattern
3270 }
3271 // user changed mind and erased the "/"- do nothing
3272 break;
3273 case 'N': // N- backward search for last pattern
3274 if (cmdcnt-- > 1) {
3275 do_cmd(c);
3276 } // repeat cnt
3277 dir = BACK; // assume BACKWARD search
3278 p = dot - 1;
3279 if (last_search_pattern[0] == '?') {
3280 dir = FORWARD;
3281 p = dot + 1;
3282 }
3283 goto dc4; // now search for pattern
3284 break;
3285 case 'n': // n- repeat search for last pattern
3286 // search rest of text[] starting at next char
3287 // if search fails return orignal "p" not the "p+1" address
3288 if (cmdcnt-- > 1) {
3289 do_cmd(c);
3290 } // repeat cnt
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003291 dc3:
Eric Andersen3f980402001-04-04 17:31:15 +00003292 if (last_search_pattern == 0) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003293 msg = "No previous regular expression";
Eric Andersen3f980402001-04-04 17:31:15 +00003294 goto dc2;
3295 }
3296 if (last_search_pattern[0] == '/') {
3297 dir = FORWARD; // assume FORWARD search
3298 p = dot + 1;
3299 }
3300 if (last_search_pattern[0] == '?') {
3301 dir = BACK;
3302 p = dot - 1;
3303 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003304 dc4:
Eric Andersen3f980402001-04-04 17:31:15 +00003305 q = char_search(p, last_search_pattern + 1, dir, FULL);
3306 if (q != NULL) {
3307 dot = q; // good search, update "dot"
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003308 msg = "";
Eric Andersen3f980402001-04-04 17:31:15 +00003309 goto dc2;
3310 }
3311 // no pattern found between "dot" and "end"- continue at top
3312 p = text;
3313 if (dir == BACK) {
3314 p = end - 1;
3315 }
3316 q = char_search(p, last_search_pattern + 1, dir, FULL);
3317 if (q != NULL) { // found something
3318 dot = q; // found new pattern- goto it
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003319 msg = "search hit BOTTOM, continuing at TOP";
Eric Andersen3f980402001-04-04 17:31:15 +00003320 if (dir == BACK) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003321 msg = "search hit TOP, continuing at BOTTOM";
Eric Andersen3f980402001-04-04 17:31:15 +00003322 }
3323 } else {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003324 msg = "Pattern not found";
Eric Andersen3f980402001-04-04 17:31:15 +00003325 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003326 dc2:
3327 if (*msg)
3328 psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003329 break;
3330 case '{': // {- move backward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003331 q = char_search(dot, "\n\n", BACK, 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;
3336 case '}': // }- move forward paragraph
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003337 q = char_search(dot, "\n\n", FORWARD, FULL);
Eric Andersen3f980402001-04-04 17:31:15 +00003338 if (q != NULL) { // found blank line
3339 dot = next_line(q); // move to next blank line
3340 }
3341 break;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003342#endif /* FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003343 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003344 case '1': // 1-
3345 case '2': // 2-
3346 case '3': // 3-
3347 case '4': // 4-
3348 case '5': // 5-
3349 case '6': // 6-
3350 case '7': // 7-
3351 case '8': // 8-
3352 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003353 if (c == '0' && cmdcnt < 1) {
3354 dot_begin(); // this was a standalone zero
3355 } else {
3356 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3357 }
3358 break;
3359 case ':': // :- the colon mode commands
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003360 p = get_input_line(":"); // get input line- use "status line"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003361#if ENABLE_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003362 colon(p); // execute the command
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003363#else
Eric Andersen822c3832001-05-07 17:37:43 +00003364 if (*p == ':')
3365 p++; // move past the ':'
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003366 cnt = strlen(p);
Eric Andersen822c3832001-05-07 17:37:43 +00003367 if (cnt <= 0)
3368 break;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003369 if (strncasecmp(p, "quit", cnt) == 0
3370 || strncasecmp(p, "q!", cnt) == 0 // delete lines
3371 ) {
Matt Kraai1f0c4362001-12-20 23:13:26 +00003372 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003373 psbs("No write since last change (:quit! overrides)");
3374 } else {
3375 editing = 0;
3376 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003377 } else if (strncasecmp(p, "write", cnt) == 0
3378 || strncasecmp(p, "wq", cnt) == 0
3379 || strncasecmp(p, "wn", cnt) == 0
3380 || strncasecmp(p, "x", cnt) == 0
3381 ) {
Eric Andersen3f980402001-04-04 17:31:15 +00003382 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003383 if (cnt < 0) {
3384 if (cnt == -1)
3385 psbs("Write error: %s", strerror(errno));
3386 } else {
3387 file_modified = 0;
3388 last_file_modified = -1;
3389 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003390 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
3391 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
3392 ) {
Paul Fox61e45db2005-10-09 14:43:22 +00003393 editing = 0;
3394 }
Eric Andersen3f980402001-04-04 17:31:15 +00003395 }
Denis Vlasenko219d14d2007-03-24 15:40:16 +00003396 } else if (strncasecmp(p, "file", cnt) == 0) {
Paul Fox8552aec2005-09-16 12:20:05 +00003397 last_status_cksum = 0; // force status update
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003398 } else if (sscanf(p, "%d", &j) > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003399 dot = find_line(j); // go to line # j
3400 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003401 } else { // unrecognised cmd
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003402 ni(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003403 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003404#endif /* !FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003405 break;
3406 case '<': // <- Left shift something
3407 case '>': // >- Right shift something
3408 cnt = count_lines(text, dot); // remember what line we are on
3409 c1 = get_one_char(); // get the type of thing to delete
3410 find_range(&p, &q, c1);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003411 yank_delete(p, q, 1, YANKONLY); // save copy before change
Eric Andersen3f980402001-04-04 17:31:15 +00003412 p = begin_line(p);
3413 q = end_line(q);
3414 i = count_lines(p, q); // # of lines we are shifting
3415 for ( ; i > 0; i--, p = next_line(p)) {
3416 if (c == '<') {
3417 // shift left- remove tab or 8 spaces
3418 if (*p == '\t') {
3419 // shrink buffer 1 char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003420 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003421 } else if (*p == ' ') {
3422 // we should be calculating columns, not just SPACE
3423 for (j = 0; *p == ' ' && j < tabstop; j++) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003424 text_hole_delete(p, p);
Eric Andersen3f980402001-04-04 17:31:15 +00003425 }
3426 }
3427 } else if (c == '>') {
3428 // shift right -- add tab or 8 spaces
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003429 char_insert(p, '\t');
Eric Andersen3f980402001-04-04 17:31:15 +00003430 }
3431 }
3432 dot = find_line(cnt); // what line were we on
3433 dot_skip_over_ws();
3434 end_cmd_q(); // stop adding to q
3435 break;
3436 case 'A': // A- append at e-o-l
3437 dot_end(); // go to e-o-l
3438 //**** fall thru to ... 'a'
3439 case 'a': // a- append after current char
3440 if (*dot != '\n')
3441 dot++;
3442 goto dc_i;
3443 break;
3444 case 'B': // B- back a blank-delimited Word
3445 case 'E': // E- end of a blank-delimited word
3446 case 'W': // W- forward a blank-delimited word
3447 if (cmdcnt-- > 1) {
3448 do_cmd(c);
3449 } // repeat cnt
3450 dir = FORWARD;
3451 if (c == 'B')
3452 dir = BACK;
3453 if (c == 'W' || isspace(dot[dir])) {
3454 dot = skip_thing(dot, 1, dir, S_TO_WS);
3455 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3456 }
3457 if (c != 'W')
3458 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3459 break;
3460 case 'C': // C- Change to e-o-l
3461 case 'D': // D- delete to e-o-l
3462 save_dot = dot;
3463 dot = dollar_line(dot); // move to before NL
3464 // copy text into a register and delete
3465 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3466 if (c == 'C')
3467 goto dc_i; // start inserting
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003468#if ENABLE_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003469 if (c == 'D')
3470 end_cmd_q(); // stop adding to q
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003471#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003472 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003473 case 'G': // G- goto to a line number (default= E-O-F)
3474 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003475 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003476 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003477 }
3478 dot_skip_over_ws();
3479 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003480 case 'H': // H- goto top line on screen
3481 dot = screenbegin;
3482 if (cmdcnt > (rows - 1)) {
3483 cmdcnt = (rows - 1);
3484 }
3485 if (cmdcnt-- > 1) {
3486 do_cmd('+');
3487 } // repeat cnt
3488 dot_skip_over_ws();
3489 break;
3490 case 'I': // I- insert before first non-blank
3491 dot_begin(); // 0
3492 dot_skip_over_ws();
3493 //**** fall thru to ... 'i'
3494 case 'i': // i- insert before current char
3495 case VI_K_INSERT: // Cursor Key Insert
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003496 dc_i:
Eric Andersen3f980402001-04-04 17:31:15 +00003497 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003498 break;
3499 case 'J': // J- join current and next lines together
3500 if (cmdcnt-- > 2) {
3501 do_cmd(c);
3502 } // repeat cnt
3503 dot_end(); // move to NL
3504 if (dot < end - 1) { // make sure not last char in text[]
3505 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003506 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003507 while (isblnk(*dot)) { // delete leading WS
3508 dot_delete();
3509 }
3510 }
3511 end_cmd_q(); // stop adding to q
3512 break;
3513 case 'L': // L- goto bottom line on screen
3514 dot = end_screen();
3515 if (cmdcnt > (rows - 1)) {
3516 cmdcnt = (rows - 1);
3517 }
3518 if (cmdcnt-- > 1) {
3519 do_cmd('-');
3520 } // repeat cnt
3521 dot_begin();
3522 dot_skip_over_ws();
3523 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003524 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003525 dot = screenbegin;
3526 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3527 dot = next_line(dot);
3528 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003529 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003530 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003531 p = begin_line(dot);
3532 if (p[-1] == '\n') {
3533 dot_prev();
3534 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3535 dot_end();
3536 dot = char_insert(dot, '\n');
3537 } else {
3538 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003539 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003540 dot_prev(); // -
3541 }
3542 goto dc_i;
3543 break;
3544 case 'R': // R- continuous Replace char
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003545 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003546 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003547 break;
3548 case 'X': // X- delete char before dot
3549 case 'x': // x- delete the current char
3550 case 's': // s- substitute the current char
3551 if (cmdcnt-- > 1) {
3552 do_cmd(c);
3553 } // repeat cnt
3554 dir = 0;
3555 if (c == 'X')
3556 dir = -1;
3557 if (dot[dir] != '\n') {
3558 if (c == 'X')
3559 dot--; // delete prev char
3560 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3561 }
3562 if (c == 's')
3563 goto dc_i; // start insrting
3564 end_cmd_q(); // stop adding to q
3565 break;
3566 case 'Z': // Z- if modified, {write}; exit
3567 // ZZ means to save file (if necessary), then exit
3568 c1 = get_one_char();
3569 if (c1 != 'Z') {
3570 indicate_error(c);
3571 break;
3572 }
Paul Foxf0305b72006-03-28 14:18:21 +00003573 if (file_modified) {
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003574#if ENABLE_FEATURE_VI_READONLY
Paul Foxf0305b72006-03-28 14:18:21 +00003575 if (vi_readonly || readonly) {
Denis Vlasenko92758142006-10-03 19:56:34 +00003576 psbs("\"%s\" File is read only", cfn);
3577 break;
Paul Foxf0305b72006-03-28 14:18:21 +00003578 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003579#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003580 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003581 if (cnt < 0) {
3582 if (cnt == -1)
3583 psbs("Write error: %s", strerror(errno));
3584 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003585 editing = 0;
3586 }
3587 } else {
3588 editing = 0;
3589 }
3590 break;
3591 case '^': // ^- move to first non-blank on line
3592 dot_begin();
3593 dot_skip_over_ws();
3594 break;
3595 case 'b': // b- back a word
3596 case 'e': // e- end of word
3597 if (cmdcnt-- > 1) {
3598 do_cmd(c);
3599 } // repeat cnt
3600 dir = FORWARD;
3601 if (c == 'b')
3602 dir = BACK;
3603 if ((dot + dir) < text || (dot + dir) > end - 1)
3604 break;
3605 dot += dir;
3606 if (isspace(*dot)) {
3607 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3608 }
3609 if (isalnum(*dot) || *dot == '_') {
3610 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3611 } else if (ispunct(*dot)) {
3612 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3613 }
3614 break;
3615 case 'c': // c- change something
3616 case 'd': // d- delete something
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003617#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003618 case 'y': // y- yank something
3619 case 'Y': // Y- Yank a line
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003620#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003621 yf = YANKDEL; // assume either "c" or "d"
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003622#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003623 if (c == 'y' || c == 'Y')
3624 yf = YANKONLY;
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003625#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003626 c1 = 'y';
3627 if (c != 'Y')
3628 c1 = get_one_char(); // get the type of thing to delete
3629 find_range(&p, &q, c1);
3630 if (c1 == 27) { // ESC- user changed mind and wants out
3631 c = c1 = 27; // Escape- do nothing
3632 } else if (strchr("wW", c1)) {
3633 if (c == 'c') {
3634 // don't include trailing WS as part of word
3635 while (isblnk(*q)) {
3636 if (q <= text || q[-1] == '\n')
3637 break;
3638 q--;
3639 }
3640 }
3641 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003642 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003643 // single line copy text into a register and delete
3644 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003645 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003646 // multiple line copy text into a register and delete
3647 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003648 if (c == 'c') {
3649 dot = char_insert(dot, '\n');
3650 // on the last line of file don't move to prev line
3651 if (dot != (end-1)) {
3652 dot_prev();
3653 }
3654 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003655 dot_begin();
3656 dot_skip_over_ws();
3657 }
3658 } else {
3659 // could not recognize object
3660 c = c1 = 27; // error-
3661 indicate_error(c);
3662 }
3663 if (c1 != 27) {
3664 // if CHANGING, not deleting, start inserting after the delete
3665 if (c == 'c') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003666 strcpy(buf, "Change");
Eric Andersen3f980402001-04-04 17:31:15 +00003667 goto dc_i; // start inserting
3668 }
3669 if (c == 'd') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003670 strcpy(buf, "Delete");
Eric Andersen3f980402001-04-04 17:31:15 +00003671 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003672#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003673 if (c == 'y' || c == 'Y') {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003674 strcpy(buf, "Yank");
Eric Andersen3f980402001-04-04 17:31:15 +00003675 }
3676 p = reg[YDreg];
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003677 q = p + strlen(p);
Eric Andersen3f980402001-04-04 17:31:15 +00003678 for (cnt = 0; p <= q; p++) {
3679 if (*p == '\n')
3680 cnt++;
3681 }
3682 psb("%s %d lines (%d chars) using [%c]",
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003683 buf, cnt, strlen(reg[YDreg]), what_reg());
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003684#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003685 end_cmd_q(); // stop adding to q
3686 }
3687 break;
3688 case 'k': // k- goto prev line, same col
3689 case VI_K_UP: // cursor key Up
3690 if (cmdcnt-- > 1) {
3691 do_cmd(c);
3692 } // repeat cnt
3693 dot_prev();
3694 dot = move_to_col(dot, ccol + offset); // try stay in same col
3695 break;
3696 case 'r': // r- replace the current char with user input
3697 c1 = get_one_char(); // get the replacement char
3698 if (*dot != '\n') {
3699 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003700 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003701 }
3702 end_cmd_q(); // stop adding to q
3703 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003704 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003705 last_forward_char = get_one_char();
3706 do_cmd(';');
3707 if (*dot == last_forward_char)
3708 dot_left();
3709 last_forward_char= 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003710 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003711 case 'w': // w- forward a word
3712 if (cmdcnt-- > 1) {
3713 do_cmd(c);
3714 } // repeat cnt
3715 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3716 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3717 } else if (ispunct(*dot)) { // we are on PUNCT
3718 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3719 }
3720 if (dot < end - 1)
3721 dot++; // move over word
3722 if (isspace(*dot)) {
3723 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3724 }
3725 break;
3726 case 'z': // z-
3727 c1 = get_one_char(); // get the replacement char
3728 cnt = 0;
3729 if (c1 == '.')
3730 cnt = (rows - 2) / 2; // put dot at center
3731 if (c1 == '-')
3732 cnt = rows - 2; // put dot at bottom
3733 screenbegin = begin_line(dot); // start dot at top
3734 dot_scroll(cnt, -1);
3735 break;
3736 case '|': // |- move to column "cmdcnt"
3737 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3738 break;
3739 case '~': // ~- flip the case of letters a-z -> A-Z
3740 if (cmdcnt-- > 1) {
3741 do_cmd(c);
3742 } // repeat cnt
3743 if (islower(*dot)) {
3744 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003745 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003746 } else if (isupper(*dot)) {
3747 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003748 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003749 }
3750 dot_right();
3751 end_cmd_q(); // stop adding to q
3752 break;
3753 //----- The Cursor and Function Keys -----------------------------
3754 case VI_K_HOME: // Cursor Key Home
3755 dot_begin();
3756 break;
3757 // The Fn keys could point to do_macro which could translate them
3758 case VI_K_FUN1: // Function Key F1
3759 case VI_K_FUN2: // Function Key F2
3760 case VI_K_FUN3: // Function Key F3
3761 case VI_K_FUN4: // Function Key F4
3762 case VI_K_FUN5: // Function Key F5
3763 case VI_K_FUN6: // Function Key F6
3764 case VI_K_FUN7: // Function Key F7
3765 case VI_K_FUN8: // Function Key F8
3766 case VI_K_FUN9: // Function Key F9
3767 case VI_K_FUN10: // Function Key F10
3768 case VI_K_FUN11: // Function Key F11
3769 case VI_K_FUN12: // Function Key F12
3770 break;
3771 }
3772
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003773 dc1:
Eric Andersen3f980402001-04-04 17:31:15 +00003774 // if text[] just became empty, add back an empty line
3775 if (end == text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003776 char_insert(text, '\n'); // start empty buf with dummy line
Eric Andersen3f980402001-04-04 17:31:15 +00003777 dot = text;
3778 }
3779 // it is OK for dot to exactly equal to end, otherwise check dot validity
3780 if (dot != end) {
3781 dot = bound_dot(dot); // make sure "dot" is valid
3782 }
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003783#if ENABLE_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003784 check_context(c); // update the current context
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003785#endif
Eric Andersen3f980402001-04-04 17:31:15 +00003786
3787 if (!isdigit(c))
3788 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3789 cnt = dot - begin_line(dot);
3790 // Try to stay off of the Newline
3791 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3792 dot--;
3793}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003794
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003795#if ENABLE_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003796static int totalcmds = 0;
3797static int Mp = 85; // Movement command Probability
3798static int Np = 90; // Non-movement command Probability
3799static int Dp = 96; // Delete command Probability
3800static int Ip = 97; // Insert command Probability
3801static int Yp = 98; // Yank command Probability
3802static int Pp = 99; // Put command Probability
3803static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003804const char chars[20] = "\t012345 abcdABCD-=.$";
3805const char *const words[20] = {
3806 "this", "is", "a", "test",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003807 "broadcast", "the", "emergency", "of",
3808 "system", "quick", "brown", "fox",
3809 "jumped", "over", "lazy", "dogs",
3810 "back", "January", "Febuary", "March"
3811};
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003812const char *const lines[20] = {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003813 "You should have received a copy of the GNU General Public License\n",
3814 "char c, cm, *cmd, *cmd1;\n",
3815 "generate a command by percentages\n",
3816 "Numbers may be typed as a prefix to some commands.\n",
3817 "Quit, discarding changes!\n",
3818 "Forced write, if permission originally not valid.\n",
3819 "In general, any ex or ed command (such as substitute or delete).\n",
3820 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3821 "Please get w/ me and I will go over it with you.\n",
3822 "The following is a list of scheduled, committed changes.\n",
3823 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3824 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3825 "Any question about transactions please contact Sterling Huxley.\n",
3826 "I will try to get back to you by Friday, December 31.\n",
3827 "This Change will be implemented on Friday.\n",
3828 "Let me know if you have problems accessing this;\n",
3829 "Sterling Huxley recently added you to the access list.\n",
3830 "Would you like to go to lunch?\n",
3831 "The last command will be automatically run.\n",
3832 "This is too much english for a computer geek.\n",
3833};
3834char *multilines[20] = {
3835 "You should have received a copy of the GNU General Public License\n",
3836 "char c, cm, *cmd, *cmd1;\n",
3837 "generate a command by percentages\n",
3838 "Numbers may be typed as a prefix to some commands.\n",
3839 "Quit, discarding changes!\n",
3840 "Forced write, if permission originally not valid.\n",
3841 "In general, any ex or ed command (such as substitute or delete).\n",
3842 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3843 "Please get w/ me and I will go over it with you.\n",
3844 "The following is a list of scheduled, committed changes.\n",
3845 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3846 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3847 "Any question about transactions please contact Sterling Huxley.\n",
3848 "I will try to get back to you by Friday, December 31.\n",
3849 "This Change will be implemented on Friday.\n",
3850 "Let me know if you have problems accessing this;\n",
3851 "Sterling Huxley recently added you to the access list.\n",
3852 "Would you like to go to lunch?\n",
3853 "The last command will be automatically run.\n",
3854 "This is too much english for a computer geek.\n",
3855};
3856
3857// create a random command to execute
3858static void crash_dummy()
3859{
3860 static int sleeptime; // how long to pause between commands
3861 char c, cm, *cmd, *cmd1;
3862 int i, cnt, thing, rbi, startrbi, percent;
3863
3864 // "dot" movement commands
3865 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3866
3867 // is there already a command running?
3868 if (readed_for_parse > 0)
3869 goto cd1;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003870 cd0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003871 startrbi = rbi = 0;
3872 sleeptime = 0; // how long to pause between commands
3873 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3874 // generate a command by percentages
3875 percent = (int) lrand48() % 100; // get a number from 0-99
3876 if (percent < Mp) { // Movement commands
3877 // available commands
3878 cmd = cmd1;
3879 M++;
3880 } else if (percent < Np) { // non-movement commands
3881 cmd = "mz<>\'\""; // available commands
3882 N++;
3883 } else if (percent < Dp) { // Delete commands
3884 cmd = "dx"; // available commands
3885 D++;
3886 } else if (percent < Ip) { // Inset commands
3887 cmd = "iIaAsrJ"; // available commands
3888 I++;
3889 } else if (percent < Yp) { // Yank commands
3890 cmd = "yY"; // available commands
3891 Y++;
3892 } else if (percent < Pp) { // Put commands
3893 cmd = "pP"; // available commands
3894 P++;
3895 } else {
3896 // We do not know how to handle this command, try again
3897 U++;
3898 goto cd0;
3899 }
3900 // randomly pick one of the available cmds from "cmd[]"
3901 i = (int) lrand48() % strlen(cmd);
3902 cm = cmd[i];
3903 if (strchr(":\024", cm))
3904 goto cd0; // dont allow colon or ctrl-T commands
3905 readbuffer[rbi++] = cm; // put cmd into input buffer
3906
3907 // now we have the command-
3908 // there are 1, 2, and multi char commands
3909 // find out which and generate the rest of command as necessary
3910 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3911 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3912 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3913 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3914 }
3915 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3916 c = cmd1[thing];
3917 readbuffer[rbi++] = c; // add movement to input buffer
3918 }
3919 if (strchr("iIaAsc", cm)) { // multi-char commands
3920 if (cm == 'c') {
3921 // change some thing
3922 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3923 c = cmd1[thing];
3924 readbuffer[rbi++] = c; // add movement to input buffer
3925 }
3926 thing = (int) lrand48() % 4; // what thing to insert
3927 cnt = (int) lrand48() % 10; // how many to insert
3928 for (i = 0; i < cnt; i++) {
3929 if (thing == 0) { // insert chars
3930 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3931 } else if (thing == 1) { // insert words
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003932 strcat(readbuffer, words[(int) lrand48() % 20]);
3933 strcat(readbuffer, " ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003934 sleeptime = 0; // how fast to type
3935 } else if (thing == 2) { // insert lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003936 strcat(readbuffer, lines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003937 sleeptime = 0; // how fast to type
3938 } else { // insert multi-lines
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003939 strcat(readbuffer, multilines[(int) lrand48() % 20]);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003940 sleeptime = 0; // how fast to type
3941 }
3942 }
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003943 strcat(readbuffer, "\033");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003944 }
3945 readed_for_parse = strlen(readbuffer);
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003946 cd1:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003947 totalcmds++;
3948 if (sleeptime > 0)
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003949 mysleep(sleeptime); // sleep 1/100 sec
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003950}
3951
3952// test to see if there are any errors
3953static void crash_test()
3954{
3955 static time_t oldtim;
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003956
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003957 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00003958 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003959
3960 msg[0] = '\0';
3961 if (end < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003962 strcat(msg, "end<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003963 }
3964 if (end > textend) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003965 strcat(msg, "end>textend ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003966 }
3967 if (dot < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003968 strcat(msg, "dot<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003969 }
3970 if (dot > end) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003971 strcat(msg, "dot>end ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003972 }
3973 if (screenbegin < text) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003974 strcat(msg, "screenbegin<text ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003975 }
3976 if (screenbegin > end - 1) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003977 strcat(msg, "screenbegin>end-1 ");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003978 }
3979
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003980 if (msg[0]) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003981 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00003982 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003983 totalcmds, last_input_char, msg, SOs, SOn);
3984 fflush(stdout);
3985 while (read(0, d, 1) > 0) {
3986 if (d[0] == '\n' || d[0] == '\r')
3987 break;
3988 }
3989 alarm(3);
3990 }
3991 tim = (time_t) time((time_t *) 0);
3992 if (tim >= (oldtim + 3)) {
Denis Vlasenkoafa37cf2007-03-21 00:05:35 +00003993 sprintf(status_buffer,
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003994 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3995 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3996 oldtim = tim;
3997 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003998}
Denis Vlasenko6a5dc5d2006-12-30 18:42:29 +00003999#endif