blob: 3bf9ac3f98643c98d088e85fec07ae02da695651 [file] [log] [blame]
Eric Andersen3f980402001-04-04 17:31:15 +00001/* vi: set sw=8 ts=8: */
2/*
3 * tiny vi.c: A small 'vi' clone
4 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
Eric Andersen044228d2001-07-17 01:12:36 +000021static const char vi_Version[] =
Eric Andersena9eb33d2004-08-19 19:15:06 +000022 "$Id: vi.c,v 1.38 2004/08/19 19:15:06 andersen Exp $";
Eric Andersen3f980402001-04-04 17:31:15 +000023
24/*
25 * To compile for standalone use:
Eric Andersend402edf2001-04-04 19:29:48 +000026 * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c
Eric Andersen3f980402001-04-04 17:31:15 +000027 * or
Eric Andersenbdfd0d72001-10-24 05:00:29 +000028 * gcc -Wall -Os -s -DSTANDALONE -DCONFIG_FEATURE_VI_CRASHME -o vi vi.c # include testing features
Eric Andersen3f980402001-04-04 17:31:15 +000029 * strip vi
30 */
31
32/*
33 * Things To Do:
34 * EXINIT
Eric Andersen1c0d3112001-04-16 15:46:44 +000035 * $HOME/.exrc and ./.exrc
Eric Andersen3f980402001-04-04 17:31:15 +000036 * add magic to search /foo.*bar
37 * add :help command
38 * :map macros
39 * how about mode lines: vi: set sw=8 ts=8:
40 * if mark[] values were line numbers rather than pointers
41 * it would be easier to change the mark when add/delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +000042 * More intelligence in refresh()
43 * ":r !cmd" and "!cmd" to filter text through an external command
44 * A true "undo" facility
45 * An "ex" line oriented mode- maybe using "cmdedit"
Eric Andersen3f980402001-04-04 17:31:15 +000046 */
47
Eric Andersenaff114c2004-04-14 17:51:38 +000048//---- Feature -------------- Bytes to implement
Eric Andersen3f980402001-04-04 17:31:15 +000049#ifdef STANDALONE
Eric Andersen822c3832001-05-07 17:37:43 +000050#define vi_main main
Eric Andersenbdfd0d72001-10-24 05:00:29 +000051#define CONFIG_FEATURE_VI_COLON // 4288
52#define CONFIG_FEATURE_VI_YANKMARK // 1408
53#define CONFIG_FEATURE_VI_SEARCH // 1088
54#define CONFIG_FEATURE_VI_USE_SIGNALS // 1056
55#define CONFIG_FEATURE_VI_DOT_CMD // 576
56#define CONFIG_FEATURE_VI_READONLY // 128
57#define CONFIG_FEATURE_VI_SETOPTS // 576
58#define CONFIG_FEATURE_VI_SET // 224
59#define CONFIG_FEATURE_VI_WIN_RESIZE // 256 WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +000060// To test editor using CRASHME:
61// vi -C filename
62// To stop testing, wait until all to text[] is deleted, or
63// Ctrl-Z and kill -9 %1
64// while in the editor Ctrl-T will toggle the crashme function on and off.
Eric Andersenbdfd0d72001-10-24 05:00:29 +000065//#define CONFIG_FEATURE_VI_CRASHME // randomly pick commands to execute
Eric Andersen3f980402001-04-04 17:31:15 +000066#endif /* STANDALONE */
67
Eric Andersen3f980402001-04-04 17:31:15 +000068#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <termios.h>
72#include <unistd.h>
73#include <sys/ioctl.h>
74#include <sys/time.h>
75#include <sys/types.h>
76#include <sys/stat.h>
77#include <time.h>
78#include <fcntl.h>
79#include <signal.h>
80#include <setjmp.h>
81#include <regex.h>
82#include <ctype.h>
83#include <assert.h>
84#include <errno.h>
85#include <stdarg.h>
Eric Andersene0c07572001-06-23 13:49:14 +000086#ifndef STANDALONE
87#include "busybox.h"
88#endif /* STANDALONE */
Eric Andersen3f980402001-04-04 17:31:15 +000089
Glenn L McGrath09adaca2002-12-02 21:18:10 +000090#ifdef CONFIG_LOCALE_SUPPORT
91#define Isprint(c) isprint((c))
92#else
93#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
94#endif
95
Eric Andersen3f980402001-04-04 17:31:15 +000096#ifndef TRUE
97#define TRUE ((int)1)
98#define FALSE ((int)0)
99#endif /* TRUE */
Eric Andersen1c0d3112001-04-16 15:46:44 +0000100#define MAX_SCR_COLS BUFSIZ
Eric Andersen3f980402001-04-04 17:31:15 +0000101
102// Misc. non-Ascii keys that report an escape sequence
103#define VI_K_UP 128 // cursor key Up
104#define VI_K_DOWN 129 // cursor key Down
105#define VI_K_RIGHT 130 // Cursor Key Right
106#define VI_K_LEFT 131 // cursor key Left
107#define VI_K_HOME 132 // Cursor Key Home
108#define VI_K_END 133 // Cursor Key End
109#define VI_K_INSERT 134 // Cursor Key Insert
110#define VI_K_PAGEUP 135 // Cursor Key Page Up
111#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
112#define VI_K_FUN1 137 // Function Key F1
113#define VI_K_FUN2 138 // Function Key F2
114#define VI_K_FUN3 139 // Function Key F3
115#define VI_K_FUN4 140 // Function Key F4
116#define VI_K_FUN5 141 // Function Key F5
117#define VI_K_FUN6 142 // Function Key F6
118#define VI_K_FUN7 143 // Function Key F7
119#define VI_K_FUN8 144 // Function Key F8
120#define VI_K_FUN9 145 // Function Key F9
121#define VI_K_FUN10 146 // Function Key F10
122#define VI_K_FUN11 147 // Function Key F11
123#define VI_K_FUN12 148 // Function Key F12
124
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000125/* vt102 typical ESC sequence */
126/* terminal standout start/normal ESC sequence */
127static const char SOs[] = "\033[7m";
128static const char SOn[] = "\033[0m";
129/* terminal bell sequence */
130static const char bell[] = "\007";
131/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
132static const char Ceol[] = "\033[0K";
133static const char Ceos [] = "\033[0J";
134/* Cursor motion arbitrary destination ESC sequence */
135static const char CMrc[] = "\033[%d;%dH";
136/* Cursor motion up and down ESC sequence */
137static const char CMup[] = "\033[A";
138static const char CMdown[] = "\n";
139
140
Rob Landleybc68cd12006-03-10 19:22:06 +0000141enum {
142 YANKONLY = FALSE,
143 YANKDEL = TRUE,
144 FORWARD = 1, // code depends on "1" for array index
145 BACK = -1, // code depends on "-1" for array index
146 LIMITED = 0, // how much of text[] in char_search
147 FULL = 1, // how much of text[] in char_search
Eric Andersen3f980402001-04-04 17:31:15 +0000148
Rob Landleybc68cd12006-03-10 19:22:06 +0000149 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
150 S_TO_WS = 2, // used in skip_thing() for moving "dot"
151 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
152 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
153 S_END_ALNUM = 5 // used in skip_thing() for moving "dot"
154};
Eric Andersen3f980402001-04-04 17:31:15 +0000155
156typedef unsigned char Byte;
157
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000158static int vi_setops;
159#define VI_AUTOINDENT 1
160#define VI_SHOWMATCH 2
161#define VI_IGNORECASE 4
162#define VI_ERR_METHOD 8
163#define autoindent (vi_setops & VI_AUTOINDENT)
164#define showmatch (vi_setops & VI_SHOWMATCH )
165#define ignorecase (vi_setops & VI_IGNORECASE)
166/* indicate error with beep or flash */
167#define err_method (vi_setops & VI_ERR_METHOD)
168
Eric Andersen3f980402001-04-04 17:31:15 +0000169
170static int editing; // >0 while we are editing a file
Paul Fox8552aec2005-09-16 12:20:05 +0000171static int cmd_mode; // 0=command 1=insert 2=replace
Eric Andersen3f980402001-04-04 17:31:15 +0000172static int file_modified; // buffer contents changed
Paul Fox8552aec2005-09-16 12:20:05 +0000173static int last_file_modified = -1;
Eric Andersen3f980402001-04-04 17:31:15 +0000174static int fn_start; // index of first cmd line file name
175static int save_argc; // how many file names on cmd line
176static int cmdcnt; // repetition count
177static fd_set rfds; // use select() for small sleeps
178static struct timeval tv; // use select() for small sleeps
Eric Andersen3f980402001-04-04 17:31:15 +0000179static int rows, columns; // the terminal screen is this size
180static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
Eric Andersen3f980402001-04-04 17:31:15 +0000181static Byte *status_buffer; // mesages to the user
Paul Fox8552aec2005-09-16 12:20:05 +0000182#define STATUS_BUFFER_LEN 200
183static int have_status_msg; // is default edit status needed?
184static int last_status_cksum; // hash of current status line
Eric Andersen3f980402001-04-04 17:31:15 +0000185static Byte *cfn; // previous, current, and next file name
186static Byte *text, *end, *textend; // pointers to the user data in memory
187static Byte *screen; // pointer to the virtual screen buffer
188static int screensize; // and its size
189static Byte *screenbegin; // index into text[], of top line on the screen
190static Byte *dot; // where all the action takes place
191static int tabstop;
192static struct termios term_orig, term_vi; // remember what the cooked mode was
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000193static Byte erase_char; // the users erase character
194static Byte last_input_char; // last char read from user
195static Byte last_forward_char; // last char searched for with 'f'
Eric Andersen3f980402001-04-04 17:31:15 +0000196
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000197#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
Eric Andersen822c3832001-05-07 17:37:43 +0000198static int last_row; // where the cursor was last moved to
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000199#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
200#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000201static jmp_buf restart; // catch_sig()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000202#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000203#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
204static int my_pid;
205#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000206#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000207static int adding2q; // are we currently adding user input to q
208static Byte *last_modifying_cmd; // last modifying cmd for "."
209static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000210#endif /* CONFIG_FEATURE_VI_DOT_CMD */
211#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000212static Byte *modifying_cmds; // cmds that modify text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000213#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
214#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000215static int vi_readonly, readonly;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000216#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000217#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000218static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
219static int YDreg, Ureg; // default delete register and orig line for "U"
220static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
221static Byte *context_start, *context_end;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000222#endif /* CONFIG_FEATURE_VI_YANKMARK */
223#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000224static Byte *last_search_pattern; // last pattern from a '/' or '?' search
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000225#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +0000226
227
228static void edit_file(Byte *); // edit one file
229static void do_cmd(Byte); // execute a command
230static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
231static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
232static Byte *end_line(Byte *); // return pointer to cur line E-o-l
Eric Andersen3f980402001-04-04 17:31:15 +0000233static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
234static Byte *next_line(Byte *); // return pointer to next line B-o-l
235static Byte *end_screen(void); // get pointer to last char on screen
236static int count_lines(Byte *, Byte *); // count line from start to stop
237static Byte *find_line(int); // find begining of line #li
238static Byte *move_to_col(Byte *, int); // move "p" to column l
239static int isblnk(Byte); // is the char a blank or tab
240static void dot_left(void); // move dot left- dont leave line
241static void dot_right(void); // move dot right- dont leave line
242static void dot_begin(void); // move dot to B-o-l
243static void dot_end(void); // move dot to E-o-l
244static void dot_next(void); // move dot to next line B-o-l
245static void dot_prev(void); // move dot to prev line B-o-l
246static void dot_scroll(int, int); // move the screen up or down
247static void dot_skip_over_ws(void); // move dot pat WS
248static void dot_delete(void); // delete the char at 'dot'
249static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
250static Byte *new_screen(int, int); // malloc virtual screen memory
251static Byte *new_text(int); // malloc memory for text[] buffer
252static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
253static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
254static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
255static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
256static Byte *skip_thing(Byte *, int, int, int); // skip some object
257static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
258static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
259static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
260static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
261static void show_help(void); // display some help info
Eric Andersen3f980402001-04-04 17:31:15 +0000262static void rawmode(void); // set "raw" mode on tty
263static void cookmode(void); // return to "cooked" mode on tty
264static int mysleep(int); // sleep for 'h' 1/100 seconds
265static Byte readit(void); // read (maybe cursor) key from stdin
266static Byte get_one_char(void); // read 1 char from stdin
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000267static int file_size(const Byte *); // what is the byte size of "fn"
Eric Andersen3f980402001-04-04 17:31:15 +0000268static int file_insert(Byte *, Byte *, int);
269static int file_write(Byte *, Byte *, Byte *);
Eric Andersen822c3832001-05-07 17:37:43 +0000270static void place_cursor(int, int, int);
Eric Andersenbff7a602001-11-17 07:15:43 +0000271static void screen_erase(void);
Eric Andersen3f980402001-04-04 17:31:15 +0000272static void clear_to_eol(void);
273static void clear_to_eos(void);
274static void standout_start(void); // send "start reverse video" sequence
275static void standout_end(void); // send "end reverse video" sequence
276static void flash(int); // flash the terminal screen
Eric Andersen3f980402001-04-04 17:31:15 +0000277static void show_status_line(void); // put a message on the bottom line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000278static void psb(const char *, ...); // Print Status Buf
279static void psbs(const char *, ...); // Print Status Buf in standout mode
Eric Andersen3f980402001-04-04 17:31:15 +0000280static void ni(Byte *); // display messages
Paul Fox8552aec2005-09-16 12:20:05 +0000281static int format_edit_status(void); // format file status on status line
Eric Andersen3f980402001-04-04 17:31:15 +0000282static void redraw(int); // force a full screen refresh
Eric Andersen822c3832001-05-07 17:37:43 +0000283static void format_line(Byte*, Byte*, int);
Eric Andersen3f980402001-04-04 17:31:15 +0000284static void refresh(int); // update the terminal from screen[]
285
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000286static void Indicate_Error(void); // use flash or beep to indicate error
287#define indicate_error(c) Indicate_Error()
Paul Fox90372ed2005-10-09 14:26:26 +0000288static void Hit_Return(void);
289
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000290#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +0000291static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
292static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000293#endif /* CONFIG_FEATURE_VI_SEARCH */
294#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen1c0d3112001-04-16 15:46:44 +0000295static Byte *get_one_address(Byte *, int *); // get colon addr, if present
296static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
Eric Andersen3f980402001-04-04 17:31:15 +0000297static void colon(Byte *); // execute the "colon" mode cmds
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000298#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000299#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000300static void winch_sig(int); // catch window size changes
301static void suspend_sig(int); // catch ctrl-Z
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000302static void catch_sig(int); // catch ctrl-C and alarm time-outs
Eric Andersen3f980402001-04-04 17:31:15 +0000303static void core_sig(int); // catch a core dump signal
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000304#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
305#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000306static void start_new_cmd_q(Byte); // new queue for command
Eric Andersenbff7a602001-11-17 07:15:43 +0000307static void end_cmd_q(void); // stop saving input chars
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000308#else /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000309#define end_cmd_q()
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000310#endif /* CONFIG_FEATURE_VI_DOT_CMD */
311#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000312static void window_size_get(int); // find out what size the window is
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000313#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
314#ifdef CONFIG_FEATURE_VI_SETOPTS
Eric Andersen3f980402001-04-04 17:31:15 +0000315static void showmatching(Byte *); // show the matching pair () [] {}
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000316#endif /* CONFIG_FEATURE_VI_SETOPTS */
317#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
Eric Andersen3f980402001-04-04 17:31:15 +0000318static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000319#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
320#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000321static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
322static Byte what_reg(void); // what is letter of current YDreg
323static void check_context(Byte); // remember context for '' command
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000324#endif /* CONFIG_FEATURE_VI_YANKMARK */
325#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000326static void crash_dummy();
327static void crash_test();
328static int crashme = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000329#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000330
331
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000332static void write1(const char *out)
333{
334 fputs(out, stdout);
335}
336
Rob Landleydfba7412006-03-06 20:47:33 +0000337int vi_main(int argc, char **argv)
Eric Andersen3f980402001-04-04 17:31:15 +0000338{
Eric Andersend402edf2001-04-04 19:29:48 +0000339 int c;
Paul Fox8552aec2005-09-16 12:20:05 +0000340 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
Eric Andersen3f980402001-04-04 17:31:15 +0000341
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000342#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000343 int i;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000344#endif /* CONFIG_FEATURE_VI_YANKMARK */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000345#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
346 my_pid = getpid();
347#endif
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000348#ifdef CONFIG_FEATURE_VI_CRASHME
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000349 (void) srand((long) my_pid);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000350#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000351
Eric Andersena68ea1c2006-01-30 22:48:39 +0000352 status_buffer = (Byte *)STATUS_BUFFER;
Paul Fox8552aec2005-09-16 12:20:05 +0000353 last_status_cksum = 0;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000354
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000355#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen822c3832001-05-07 17:37:43 +0000356 vi_readonly = readonly = FALSE;
Eric Andersen3f980402001-04-04 17:31:15 +0000357 if (strncmp(argv[0], "view", 4) == 0) {
358 readonly = TRUE;
Eric Andersen822c3832001-05-07 17:37:43 +0000359 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000360 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000361#endif /* CONFIG_FEATURE_VI_READONLY */
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000362 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000363#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000364 for (i = 0; i < 28; i++) {
365 reg[i] = 0;
366 } // init the yank regs
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000367#endif /* CONFIG_FEATURE_VI_YANKMARK */
Tim Riker86c76a92002-04-26 07:41:22 +0000368#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
Eric Andersen3f980402001-04-04 17:31:15 +0000369 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000370#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000371
372 // 1- process $HOME/.exrc file
373 // 2- process EXINIT variable from environment
374 // 3- process command line args
375 while ((c = getopt(argc, argv, "hCR")) != -1) {
376 switch (c) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000377#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000378 case 'C':
379 crashme = 1;
380 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000381#endif /* CONFIG_FEATURE_VI_CRASHME */
382#ifdef CONFIG_FEATURE_VI_READONLY
Eric Andersen3f980402001-04-04 17:31:15 +0000383 case 'R': // Read-only flag
384 readonly = TRUE;
Eric Andersen90fb65f2004-03-31 11:12:51 +0000385 vi_readonly = TRUE;
Eric Andersen3f980402001-04-04 17:31:15 +0000386 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000387#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen822c3832001-05-07 17:37:43 +0000388 //case 'r': // recover flag- ignore- we don't use tmp file
389 //case 'x': // encryption flag- ignore
390 //case 'c': // execute command first
391 //case 'h': // help -- just use default
Eric Andersen3f980402001-04-04 17:31:15 +0000392 default:
393 show_help();
Eric Andersendd8500b2001-07-02 18:06:14 +0000394 return 1;
Eric Andersen3f980402001-04-04 17:31:15 +0000395 }
396 }
397
398 // The argv array can be used by the ":next" and ":rewind" commands
399 // save optind.
400 fn_start = optind; // remember first file name for :next and :rew
401 save_argc = argc;
402
403 //----- This is the main file handling loop --------------
404 if (optind >= argc) {
405 editing = 1; // 0= exit, 1= one file, 2= multiple files
406 edit_file(0);
407 } else {
408 for (; optind < argc; optind++) {
409 editing = 1; // 0=exit, 1=one file, 2+ =many files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000410 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000411 cfn = (Byte *) bb_xstrdup(argv[optind]);
Eric Andersen3f980402001-04-04 17:31:15 +0000412 edit_file(cfn);
413 }
414 }
415 //-----------------------------------------------------------
416
417 return (0);
418}
419
Eric Andersen8efe9672003-09-15 08:33:45 +0000420#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
421//----- See what the window size currently is --------------------
422static inline void window_size_get(int fd)
423{
424 get_terminal_width_height(fd, &columns, &rows);
425}
426#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
427
Eric Andersen3f980402001-04-04 17:31:15 +0000428static void edit_file(Byte * fn)
429{
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000430 Byte c;
Eric Andersen1c0d3112001-04-16 15:46:44 +0000431 int cnt, size, ch;
Eric Andersen3f980402001-04-04 17:31:15 +0000432
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000433#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +0000434 int sig;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000435#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
436#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000437 static Byte *cur_line;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000438#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000439
440 rawmode();
441 rows = 24;
442 columns = 80;
Eric Andersen822c3832001-05-07 17:37:43 +0000443 ch= -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000444#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
Eric Andersen3f980402001-04-04 17:31:15 +0000445 window_size_get(0);
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000446#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
Eric Andersen3f980402001-04-04 17:31:15 +0000447 new_screen(rows, columns); // get memory for virtual screen
448
449 cnt = file_size(fn); // file size
450 size = 2 * cnt; // 200% of file size
451 new_text(size); // get a text[] buffer
452 screenbegin = dot = end = text;
453 if (fn != 0) {
Eric Andersen1c0d3112001-04-16 15:46:44 +0000454 ch= file_insert(fn, text, cnt);
455 }
456 if (ch < 1) {
Eric Andersen3f980402001-04-04 17:31:15 +0000457 (void) char_insert(text, '\n'); // start empty buf with dummy line
458 }
Paul Fox8552aec2005-09-16 12:20:05 +0000459 file_modified = 0;
460 last_file_modified = -1;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000461#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000462 YDreg = 26; // default Yank/Delete reg
463 Ureg = 27; // hold orig line for "U" cmd
464 for (cnt = 0; cnt < 28; cnt++) {
465 mark[cnt] = 0;
466 } // init the marks
467 mark[26] = mark[27] = text; // init "previous context"
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000468#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +0000469
Eric Andersen3f980402001-04-04 17:31:15 +0000470 last_forward_char = last_input_char = '\0';
471 crow = 0;
472 ccol = 0;
Eric Andersen3f980402001-04-04 17:31:15 +0000473
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000474#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000475 catch_sig(0);
476 core_sig(0);
Eric Andersen3f980402001-04-04 17:31:15 +0000477 signal(SIGWINCH, winch_sig);
478 signal(SIGTSTP, suspend_sig);
479 sig = setjmp(restart);
480 if (sig != 0) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000481 const char *msg = "";
482
Eric Andersen3f980402001-04-04 17:31:15 +0000483 if (sig == SIGWINCH)
484 msg = "(window resize)";
485 if (sig == SIGHUP)
486 msg = "(hangup)";
487 if (sig == SIGINT)
488 msg = "(interrupt)";
489 if (sig == SIGTERM)
490 msg = "(terminate)";
491 if (sig == SIGBUS)
492 msg = "(bus error)";
493 if (sig == SIGSEGV)
494 msg = "(I tried to touch invalid memory)";
495 if (sig == SIGALRM)
496 msg = "(alarm)";
497
498 psbs("-- caught signal %d %s--", sig, msg);
Eric Andersen1c0d3112001-04-16 15:46:44 +0000499 screenbegin = dot = text;
Eric Andersen3f980402001-04-04 17:31:15 +0000500 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000501#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +0000502
503 editing = 1;
504 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
505 cmdcnt = 0;
506 tabstop = 8;
507 offset = 0; // no horizontal offset
508 c = '\0';
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000509#ifdef CONFIG_FEATURE_VI_DOT_CMD
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000510 free(last_modifying_cmd);
511 free(ioq_start);
Eric Andersen3f980402001-04-04 17:31:15 +0000512 ioq = ioq_start = last_modifying_cmd = 0;
513 adding2q = 0;
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000514#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen822c3832001-05-07 17:37:43 +0000515 redraw(FALSE); // dont force every col re-draw
Eric Andersen3f980402001-04-04 17:31:15 +0000516 show_status_line();
517
518 //------This is the main Vi cmd handling loop -----------------------
519 while (editing > 0) {
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000520#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000521 if (crashme > 0) {
522 if ((end - text) > 1) {
523 crash_dummy(); // generate a random command
524 } else {
525 crashme = 0;
526 dot =
527 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
528 refresh(FALSE);
529 }
530 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000531#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000532 last_input_char = c = get_one_char(); // get a cmd from user
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000533#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +0000534 // save a copy of the current line- for the 'U" command
535 if (begin_line(dot) != cur_line) {
536 cur_line = begin_line(dot);
537 text_yank(begin_line(dot), end_line(dot), Ureg);
538 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000539#endif /* CONFIG_FEATURE_VI_YANKMARK */
540#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +0000541 // These are commands that change text[].
542 // Remember the input for the "." command
543 if (!adding2q && ioq_start == 0
544 && strchr((char *) modifying_cmds, c) != NULL) {
545 start_new_cmd_q(c);
546 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000547#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +0000548 do_cmd(c); // execute the user command
549 //
550 // poll to see if there is input already waiting. if we are
551 // not able to display output fast enough to keep up, skip
552 // the display update until we catch up with input.
553 if (mysleep(0) == 0) {
554 // no input pending- so update output
555 refresh(FALSE);
556 show_status_line();
557 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000558#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen3f980402001-04-04 17:31:15 +0000559 if (crashme > 0)
560 crash_test(); // test editor variables
Eric Andersenbdfd0d72001-10-24 05:00:29 +0000561#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen3f980402001-04-04 17:31:15 +0000562 }
563 //-------------------------------------------------------------------
564
Eric Andersen822c3832001-05-07 17:37:43 +0000565 place_cursor(rows, 0, FALSE); // go to bottom of screen
Eric Andersen3f980402001-04-04 17:31:15 +0000566 clear_to_eol(); // Erase to end of line
567 cookmode();
568}
569
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000570//----- The Colon commands -------------------------------------
571#ifdef CONFIG_FEATURE_VI_COLON
572static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
573{
574 int st;
575 Byte *q;
576
577#ifdef CONFIG_FEATURE_VI_YANKMARK
578 Byte c;
579#endif /* CONFIG_FEATURE_VI_YANKMARK */
580#ifdef CONFIG_FEATURE_VI_SEARCH
581 Byte *pat, buf[BUFSIZ];
582#endif /* CONFIG_FEATURE_VI_SEARCH */
583
584 *addr = -1; // assume no addr
585 if (*p == '.') { // the current line
586 p++;
587 q = begin_line(dot);
588 *addr = count_lines(text, q);
589#ifdef CONFIG_FEATURE_VI_YANKMARK
590 } else if (*p == '\'') { // is this a mark addr
591 p++;
592 c = tolower(*p);
593 p++;
594 if (c >= 'a' && c <= 'z') {
595 // we have a mark
596 c = c - 'a';
597 q = mark[(int) c];
598 if (q != NULL) { // is mark valid
599 *addr = count_lines(text, q); // count lines
600 }
601 }
602#endif /* CONFIG_FEATURE_VI_YANKMARK */
603#ifdef CONFIG_FEATURE_VI_SEARCH
604 } else if (*p == '/') { // a search pattern
605 q = buf;
606 for (p++; *p; p++) {
607 if (*p == '/')
608 break;
609 *q++ = *p;
610 *q = '\0';
611 }
Manuel Novoa III cad53642003-03-19 09:13:01 +0000612 pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000613 if (*p == '/')
614 p++;
615 q = char_search(dot, pat, FORWARD, FULL);
616 if (q != NULL) {
617 *addr = count_lines(text, q);
618 }
619 free(pat);
620#endif /* CONFIG_FEATURE_VI_SEARCH */
621 } else if (*p == '$') { // the last line in file
622 p++;
623 q = begin_line(end - 1);
624 *addr = count_lines(text, q);
625 } else if (isdigit(*p)) { // specific line number
626 sscanf((char *) p, "%d%n", addr, &st);
627 p += st;
628 } else { // I don't reconise this
629 // unrecognised address- assume -1
630 *addr = -1;
631 }
632 return (p);
633}
634
635static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
636{
637 //----- get the address' i.e., 1,3 'a,'b -----
638 // get FIRST addr, if present
639 while (isblnk(*p))
640 p++; // skip over leading spaces
641 if (*p == '%') { // alias for 1,$
642 p++;
643 *b = 1;
644 *e = count_lines(text, end-1);
645 goto ga0;
646 }
647 p = get_one_address(p, b);
648 while (isblnk(*p))
649 p++;
Eric Andersenaff114c2004-04-14 17:51:38 +0000650 if (*p == ',') { // is there a address separator
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000651 p++;
652 while (isblnk(*p))
653 p++;
654 // get SECOND addr, if present
655 p = get_one_address(p, e);
656 }
657ga0:
658 while (isblnk(*p))
659 p++; // skip over trailing spaces
660 return (p);
661}
662
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000663#ifdef CONFIG_FEATURE_VI_SETOPTS
664static void setops(const Byte *args, const char *opname, int flg_no,
665 const char *short_opname, int opt)
666{
667 const char *a = (char *) args + flg_no;
668 int l = strlen(opname) - 1; /* opname have + ' ' */
669
670 if (strncasecmp(a, opname, l) == 0 ||
671 strncasecmp(a, short_opname, 2) == 0) {
672 if(flg_no)
673 vi_setops &= ~opt;
674 else
675 vi_setops |= opt;
676 }
677}
678#endif
679
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000680static void colon(Byte * buf)
681{
682 Byte c, *orig_buf, *buf1, *q, *r;
683 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
684 int i, l, li, ch, st, b, e;
Eric Andersena9eb33d2004-08-19 19:15:06 +0000685 int useforce = FALSE, forced = FALSE;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000686 struct stat st_buf;
687
688 // :3154 // if (-e line 3154) goto it else stay put
689 // :4,33w! foo // write a portion of buffer to file "foo"
690 // :w // write all of buffer to current file
691 // :q // quit
692 // :q! // quit- dont care about modified file
693 // :'a,'z!sort -u // filter block through sort
694 // :'f // goto mark "f"
695 // :'fl // list literal the mark "f" line
696 // :.r bar // read file "bar" into buffer before dot
697 // :/123/,/abc/d // delete lines from "123" line to "abc" line
698 // :/xyz/ // goto the "xyz" line
699 // :s/find/replace/ // substitute pattern "find" with "replace"
700 // :!<cmd> // run <cmd> then return
701 //
Eric Andersen165e8cb2004-07-20 06:44:46 +0000702
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000703 if (strlen((char *) buf) <= 0)
704 goto vc1;
705 if (*buf == ':')
706 buf++; // move past the ':'
707
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000708 li = st = ch = i = 0;
709 b = e = -1;
710 q = text; // assume 1,$ for the range
711 r = end - 1;
712 li = count_lines(text, end - 1);
713 fn = cfn; // default to current file
714 memset(cmd, '\0', BUFSIZ); // clear cmd[]
715 memset(args, '\0', BUFSIZ); // clear args[]
716
717 // look for optional address(es) :. :1 :1,9 :'q,'a :%
718 buf = get_address(buf, &b, &e);
719
720 // remember orig command line
721 orig_buf = buf;
722
723 // get the COMMAND into cmd[]
724 buf1 = cmd;
725 while (*buf != '\0') {
726 if (isspace(*buf))
727 break;
728 *buf1++ = *buf++;
729 }
730 // get any ARGuments
731 while (isblnk(*buf))
732 buf++;
733 strcpy((char *) args, (char *) buf);
Eric Andersena68ea1c2006-01-30 22:48:39 +0000734 buf1 = (Byte*)last_char_is((char *)cmd, '!');
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000735 if (buf1) {
736 useforce = TRUE;
737 *buf1 = '\0'; // get rid of !
738 }
739 if (b >= 0) {
740 // if there is only one addr, then the addr
741 // is the line number of the single line the
742 // user wants. So, reset the end
743 // pointer to point at end of the "b" line
744 q = find_line(b); // what line is #b
745 r = end_line(q);
746 li = 1;
747 }
748 if (e >= 0) {
749 // we were given two addrs. change the
750 // end pointer to the addr given by user.
751 r = find_line(e); // what line is #e
752 r = end_line(r);
753 li = e - b + 1;
754 }
755 // ------------ now look for the command ------------
756 i = strlen((char *) cmd);
757 if (i == 0) { // :123CR goto line #123
758 if (b >= 0) {
759 dot = find_line(b); // what line is #b
760 dot_skip_over_ws();
761 }
762 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
763 // :!ls run the <cmd>
764 (void) alarm(0); // wait for input- no alarms
765 place_cursor(rows - 1, 0, FALSE); // go to Status line
766 clear_to_eol(); // clear the line
767 cookmode();
Eric Andersena68ea1c2006-01-30 22:48:39 +0000768 system((char*)(orig_buf+1)); // run the cmd
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000769 rawmode();
770 Hit_Return(); // let user see results
771 (void) alarm(3); // done waiting for input
772 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
773 if (b < 0) { // no addr given- use defaults
774 b = e = count_lines(text, dot);
775 }
776 psb("%d", b);
777 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
778 if (b < 0) { // no addr given- use defaults
779 q = begin_line(dot); // assume .,. for the range
780 r = end_line(dot);
781 }
782 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
783 dot_skip_over_ws();
784 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
785 int sr;
786 sr= 0;
787 // don't edit, if the current file has been modified
788 if (file_modified && ! useforce) {
789 psbs("No write since last change (:edit! overrides)");
790 goto vc1;
791 }
Eric Andersena68ea1c2006-01-30 22:48:39 +0000792 if (strlen((char*)args) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000793 // the user supplied a file name
794 fn= args;
Eric Andersena68ea1c2006-01-30 22:48:39 +0000795 } else if (cfn != 0 && strlen((char*)cfn) > 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000796 // no user supplied name- use the current filename
797 fn= cfn;
798 goto vc5;
799 } else {
800 // no user file name, no current name- punt
801 psbs("No current filename");
802 goto vc1;
803 }
804
805 // see if file exists- if not, its just a new file request
806 if ((sr=stat((char*)fn, &st_buf)) < 0) {
807 // This is just a request for a new file creation.
808 // The file_insert below will fail but we get
809 // an empty buffer with a file name. Then the "write"
810 // command can do the create.
811 } else {
812 if ((st_buf.st_mode & (S_IFREG)) == 0) {
813 // This is not a regular file
814 psbs("\"%s\" is not a regular file", fn);
815 goto vc1;
816 }
817 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
818 // dont have any read permissions
819 psbs("\"%s\" is not readable", fn);
820 goto vc1;
821 }
822 }
823
824 // There is a read-able regular file
825 // make this the current file
Manuel Novoa III cad53642003-03-19 09:13:01 +0000826 q = (Byte *) bb_xstrdup((char *) fn); // save the cfn
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000827 free(cfn); // free the old name
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000828 cfn = q; // remember new cfn
829
830 vc5:
831 // delete all the contents of text[]
832 new_text(2 * file_size(fn));
833 screenbegin = dot = end = text;
834
835 // insert new file
836 ch = file_insert(fn, text, file_size(fn));
837
838 if (ch < 1) {
839 // start empty buf with dummy line
840 (void) char_insert(text, '\n');
841 ch= 1;
842 }
Paul Fox8552aec2005-09-16 12:20:05 +0000843 file_modified = 0;
844 last_file_modified = -1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000845#ifdef CONFIG_FEATURE_VI_YANKMARK
846 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
847 free(reg[Ureg]); // free orig line reg- for 'U'
848 reg[Ureg]= 0;
849 }
850 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
851 free(reg[YDreg]); // free default yank/delete register
852 reg[YDreg]= 0;
853 }
854 for (li = 0; li < 28; li++) {
855 mark[li] = 0;
856 } // init the marks
857#endif /* CONFIG_FEATURE_VI_YANKMARK */
858 // how many lines in text[]?
859 li = count_lines(text, end - 1);
860 psb("\"%s\"%s"
861#ifdef CONFIG_FEATURE_VI_READONLY
862 "%s"
863#endif /* CONFIG_FEATURE_VI_READONLY */
864 " %dL, %dC", cfn,
865 (sr < 0 ? " [New file]" : ""),
866#ifdef CONFIG_FEATURE_VI_READONLY
867 ((vi_readonly || readonly) ? " [Read only]" : ""),
868#endif /* CONFIG_FEATURE_VI_READONLY */
869 li, ch);
870 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
871 if (b != -1 || e != -1) {
872 ni((Byte *) "No address allowed on this command");
873 goto vc1;
874 }
875 if (strlen((char *) args) > 0) {
876 // user wants a new filename
Aaron Lehmanna170e1c2002-11-28 11:27:31 +0000877 free(cfn);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000878 cfn = (Byte *) bb_xstrdup((char *) args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000879 } else {
880 // user wants file status info
Paul Fox8552aec2005-09-16 12:20:05 +0000881 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000882 }
883 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
884 // print out values of all features
885 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
886 clear_to_eol(); // clear the line
887 cookmode();
888 show_help();
889 rawmode();
890 Hit_Return();
891 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
892 if (b < 0) { // no addr given- use defaults
893 q = begin_line(dot); // assume .,. for the range
894 r = end_line(dot);
895 }
896 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
897 clear_to_eol(); // clear the line
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000898 puts("\r");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000899 for (; q <= r; q++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000900 int c_is_no_print;
901
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000902 c = *q;
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000903 c_is_no_print = c > 127 && !Isprint(c);
904 if (c_is_no_print) {
905 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000906 standout_start();
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000907 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000908 if (c == '\n') {
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000909 write1("$\r");
910 } else if (c < ' ' || c == 127) {
911 putchar('^');
912 if(c == 127)
913 c = '?';
914 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000915 c += '@';
916 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +0000917 putchar(c);
918 if (c_is_no_print)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000919 standout_end();
920 }
921#ifdef CONFIG_FEATURE_VI_SET
922 vc2:
923#endif /* CONFIG_FEATURE_VI_SET */
924 Hit_Return();
925 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
926 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
927 if (useforce) {
928 // force end of argv list
929 if (*cmd == 'q') {
930 optind = save_argc;
931 }
932 editing = 0;
933 goto vc1;
934 }
935 // don't exit if the file been modified
936 if (file_modified) {
937 psbs("No write since last change (:%s! overrides)",
938 (*cmd == 'q' ? "quit" : "next"));
939 goto vc1;
940 }
941 // are there other file to edit
942 if (*cmd == 'q' && optind < save_argc - 1) {
943 psbs("%d more file to edit", (save_argc - optind - 1));
944 goto vc1;
945 }
946 if (*cmd == 'n' && optind >= save_argc - 1) {
947 psbs("No more files to edit");
948 goto vc1;
949 }
950 editing = 0;
951 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
952 fn = args;
953 if (strlen((char *) fn) <= 0) {
954 psbs("No filename given");
955 goto vc1;
956 }
957 if (b < 0) { // no addr given- use defaults
958 q = begin_line(dot); // assume "dot"
959 }
960 // read after current line- unless user said ":0r foo"
961 if (b != 0)
962 q = next_line(q);
963#ifdef CONFIG_FEATURE_VI_READONLY
964 l= readonly; // remember current files' status
965#endif
966 ch = file_insert(fn, q, file_size(fn));
967#ifdef CONFIG_FEATURE_VI_READONLY
968 readonly= l;
969#endif
970 if (ch < 0)
971 goto vc1; // nothing was inserted
972 // how many lines in text[]?
973 li = count_lines(q, q + ch - 1);
974 psb("\"%s\""
975#ifdef CONFIG_FEATURE_VI_READONLY
976 "%s"
977#endif /* CONFIG_FEATURE_VI_READONLY */
978 " %dL, %dC", fn,
979#ifdef CONFIG_FEATURE_VI_READONLY
980 ((vi_readonly || readonly) ? " [Read only]" : ""),
981#endif /* CONFIG_FEATURE_VI_READONLY */
982 li, ch);
983 if (ch > 0) {
984 // if the insert is before "dot" then we need to update
985 if (q <= dot)
986 dot += ch;
Paul Fox8552aec2005-09-16 12:20:05 +0000987 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +0000988 }
989 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
990 if (file_modified && ! useforce) {
991 psbs("No write since last change (:rewind! overrides)");
992 } else {
993 // reset the filenames to edit
994 optind = fn_start - 1;
995 editing = 0;
996 }
997#ifdef CONFIG_FEATURE_VI_SET
998 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
999 i = 0; // offset into args
1000 if (strlen((char *) args) == 0) {
1001 // print out values of all options
1002 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
1003 clear_to_eol(); // clear the line
1004 printf("----------------------------------------\r\n");
1005#ifdef CONFIG_FEATURE_VI_SETOPTS
1006 if (!autoindent)
1007 printf("no");
1008 printf("autoindent ");
1009 if (!err_method)
1010 printf("no");
1011 printf("flash ");
1012 if (!ignorecase)
1013 printf("no");
1014 printf("ignorecase ");
1015 if (!showmatch)
1016 printf("no");
1017 printf("showmatch ");
1018 printf("tabstop=%d ", tabstop);
1019#endif /* CONFIG_FEATURE_VI_SETOPTS */
1020 printf("\r\n");
1021 goto vc2;
1022 }
1023 if (strncasecmp((char *) args, "no", 2) == 0)
1024 i = 2; // ":set noautoindent"
1025#ifdef CONFIG_FEATURE_VI_SETOPTS
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001026 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
1027 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
1028 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
1029 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
1030 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001031 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
1032 if (ch > 0 && ch < columns - 1)
1033 tabstop = ch;
1034 }
1035#endif /* CONFIG_FEATURE_VI_SETOPTS */
1036#endif /* CONFIG_FEATURE_VI_SET */
1037#ifdef CONFIG_FEATURE_VI_SEARCH
1038 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
1039 Byte *ls, *F, *R;
1040 int gflag;
1041
1042 // F points to the "find" pattern
1043 // R points to the "replace" pattern
1044 // replace the cmd line delimiters "/" with NULLs
1045 gflag = 0; // global replace flag
1046 c = orig_buf[1]; // what is the delimiter
1047 F = orig_buf + 2; // start of "find"
1048 R = (Byte *) strchr((char *) F, c); // middle delimiter
1049 if (!R) goto colon_s_fail;
1050 *R++ = '\0'; // terminate "find"
1051 buf1 = (Byte *) strchr((char *) R, c);
1052 if (!buf1) goto colon_s_fail;
1053 *buf1++ = '\0'; // terminate "replace"
1054 if (*buf1 == 'g') { // :s/foo/bar/g
1055 buf1++;
1056 gflag++; // turn on gflag
1057 }
1058 q = begin_line(q);
1059 if (b < 0) { // maybe :s/foo/bar/
1060 q = begin_line(dot); // start with cur line
1061 b = count_lines(text, q); // cur line number
1062 }
1063 if (e < 0)
1064 e = b; // maybe :.s/foo/bar/
1065 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
1066 ls = q; // orig line start
1067 vc4:
1068 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
1069 if (buf1 != NULL) {
1070 // we found the "find" pattern- delete it
1071 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
1072 // inset the "replace" patern
1073 (void) string_insert(buf1, R); // insert the string
1074 // check for "global" :s/foo/bar/g
1075 if (gflag == 1) {
1076 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
1077 q = buf1 + strlen((char *) R);
1078 goto vc4; // don't let q move past cur line
1079 }
1080 }
1081 }
1082 q = next_line(ls);
1083 }
1084#endif /* CONFIG_FEATURE_VI_SEARCH */
1085 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1086 psb("%s", vi_Version);
1087 } else if ((strncasecmp((char *) cmd, "write", i) == 0) || // write text to file
1088 (strncasecmp((char *) cmd, "wq", i) == 0) ||
1089 (strncasecmp((char *) cmd, "x", i) == 0)) {
1090 // is there a file name to write to?
1091 if (strlen((char *) args) > 0) {
1092 fn = args;
1093 }
1094#ifdef CONFIG_FEATURE_VI_READONLY
1095 if ((vi_readonly || readonly) && ! useforce) {
1096 psbs("\"%s\" File is read only", fn);
1097 goto vc3;
1098 }
1099#endif /* CONFIG_FEATURE_VI_READONLY */
1100 // how many lines in text[]?
1101 li = count_lines(q, r);
1102 ch = r - q + 1;
1103 // see if file exists- if not, its just a new file request
1104 if (useforce) {
1105 // if "fn" is not write-able, chmod u+w
1106 // sprintf(syscmd, "chmod u+w %s", fn);
1107 // system(syscmd);
1108 forced = TRUE;
1109 }
1110 l = file_write(fn, q, r);
1111 if (useforce && forced) {
1112 // chmod u-w
1113 // sprintf(syscmd, "chmod u-w %s", fn);
1114 // system(syscmd);
1115 forced = FALSE;
1116 }
Paul Fox61e45db2005-10-09 14:43:22 +00001117 if (l < 0) {
1118 if (l == -1)
1119 psbs("Write error: %s", strerror(errno));
1120 } else {
1121 psb("\"%s\" %dL, %dC", fn, li, l);
1122 if (q == text && r == end - 1 && l == ch) {
1123 file_modified = 0;
1124 last_file_modified = -1;
1125 }
1126 if ((cmd[0] == 'x' || cmd[1] == 'q') && l == ch) {
1127 editing = 0;
1128 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001129 }
1130#ifdef CONFIG_FEATURE_VI_READONLY
1131 vc3:;
1132#endif /* CONFIG_FEATURE_VI_READONLY */
1133#ifdef CONFIG_FEATURE_VI_YANKMARK
1134 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1135 if (b < 0) { // no addr given- use defaults
1136 q = begin_line(dot); // assume .,. for the range
1137 r = end_line(dot);
1138 }
1139 text_yank(q, r, YDreg);
1140 li = count_lines(q, r);
1141 psb("Yank %d lines (%d chars) into [%c]",
1142 li, strlen((char *) reg[YDreg]), what_reg());
1143#endif /* CONFIG_FEATURE_VI_YANKMARK */
1144 } else {
1145 // cmd unknown
1146 ni((Byte *) cmd);
1147 }
1148 vc1:
1149 dot = bound_dot(dot); // make sure "dot" is valid
1150 return;
1151#ifdef CONFIG_FEATURE_VI_SEARCH
1152colon_s_fail:
1153 psb(":s expression missing delimiters");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001154#endif
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001155}
Paul Fox61e45db2005-10-09 14:43:22 +00001156
Paul Fox90372ed2005-10-09 14:26:26 +00001157#endif /* CONFIG_FEATURE_VI_COLON */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001158
1159static void Hit_Return(void)
1160{
1161 char c;
1162
1163 standout_start(); // start reverse video
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001164 write1("[Hit return to continue]");
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001165 standout_end(); // end reverse video
1166 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1167 ;
1168 redraw(TRUE); // force redraw all
1169}
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001170
1171//----- Synchronize the cursor to Dot --------------------------
1172static void sync_cursor(Byte * d, int *row, int *col)
1173{
1174 Byte *beg_cur, *end_cur; // begin and end of "d" line
1175 Byte *beg_scr, *end_scr; // begin and end of screen
1176 Byte *tp;
1177 int cnt, ro, co;
1178
1179 beg_cur = begin_line(d); // first char of cur line
1180 end_cur = end_line(d); // last char of cur line
1181
1182 beg_scr = end_scr = screenbegin; // first char of screen
1183 end_scr = end_screen(); // last char of screen
1184
1185 if (beg_cur < screenbegin) {
1186 // "d" is before top line on screen
1187 // how many lines do we have to move
1188 cnt = count_lines(beg_cur, screenbegin);
1189 sc1:
1190 screenbegin = beg_cur;
1191 if (cnt > (rows - 1) / 2) {
1192 // we moved too many lines. put "dot" in middle of screen
1193 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1194 screenbegin = prev_line(screenbegin);
1195 }
1196 }
1197 } else if (beg_cur > end_scr) {
1198 // "d" is after bottom line on screen
1199 // how many lines do we have to move
1200 cnt = count_lines(end_scr, beg_cur);
1201 if (cnt > (rows - 1) / 2)
1202 goto sc1; // too many lines
1203 for (ro = 0; ro < cnt - 1; ro++) {
1204 // move screen begin the same amount
1205 screenbegin = next_line(screenbegin);
1206 // now, move the end of screen
1207 end_scr = next_line(end_scr);
1208 end_scr = end_line(end_scr);
1209 }
1210 }
1211 // "d" is on screen- find out which row
1212 tp = screenbegin;
1213 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1214 if (tp == beg_cur)
1215 break;
1216 tp = next_line(tp);
1217 }
1218
1219 // find out what col "d" is on
1220 co = 0;
1221 do { // drive "co" to correct column
1222 if (*tp == '\n' || *tp == '\0')
1223 break;
1224 if (*tp == '\t') {
1225 // 7 - (co % 8 )
1226 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001227 } else if (*tp < ' ' || *tp == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001228 co++; // display as ^X, use 2 columns
1229 }
1230 } while (tp++ < d && ++co);
1231
1232 // "co" is the column where "dot" is.
1233 // The screen has "columns" columns.
1234 // The currently displayed columns are 0+offset -- columns+ofset
1235 // |-------------------------------------------------------------|
1236 // ^ ^ ^
1237 // offset | |------- columns ----------------|
1238 //
1239 // If "co" is already in this range then we do not have to adjust offset
1240 // but, we do have to subtract the "offset" bias from "co".
1241 // If "co" is outside this range then we have to change "offset".
1242 // If the first char of a line is a tab the cursor will try to stay
1243 // in column 7, but we have to set offset to 0.
1244
1245 if (co < 0 + offset) {
1246 offset = co;
1247 }
1248 if (co >= columns + offset) {
1249 offset = co - columns + 1;
1250 }
1251 // if the first char of the line is a tab, and "dot" is sitting on it
1252 // force offset to 0.
1253 if (d == beg_cur && *d == '\t') {
1254 offset = 0;
1255 }
1256 co -= offset;
1257
1258 *row = ro;
1259 *col = co;
1260}
1261
1262//----- Text Movement Routines ---------------------------------
1263static Byte *begin_line(Byte * p) // return pointer to first char cur line
1264{
1265 while (p > text && p[-1] != '\n')
1266 p--; // go to cur line B-o-l
1267 return (p);
1268}
1269
1270static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1271{
1272 while (p < end - 1 && *p != '\n')
1273 p++; // go to cur line E-o-l
1274 return (p);
1275}
1276
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001277static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001278{
1279 while (p < end - 1 && *p != '\n')
1280 p++; // go to cur line E-o-l
1281 // Try to stay off of the Newline
1282 if (*p == '\n' && (p - begin_line(p)) > 0)
1283 p--;
1284 return (p);
1285}
1286
1287static Byte *prev_line(Byte * p) // return pointer first char prev line
1288{
1289 p = begin_line(p); // goto begining of cur line
1290 if (p[-1] == '\n' && p > text)
1291 p--; // step to prev line
1292 p = begin_line(p); // goto begining of prev line
1293 return (p);
1294}
1295
1296static Byte *next_line(Byte * p) // return pointer first char next line
1297{
1298 p = end_line(p);
1299 if (*p == '\n' && p < end - 1)
1300 p++; // step to next line
1301 return (p);
1302}
1303
1304//----- Text Information Routines ------------------------------
1305static Byte *end_screen(void)
1306{
1307 Byte *q;
1308 int cnt;
1309
1310 // find new bottom line
1311 q = screenbegin;
1312 for (cnt = 0; cnt < rows - 2; cnt++)
1313 q = next_line(q);
1314 q = end_line(q);
1315 return (q);
1316}
1317
1318static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1319{
1320 Byte *q;
1321 int cnt;
1322
1323 if (stop < start) { // start and stop are backwards- reverse them
1324 q = start;
1325 start = stop;
1326 stop = q;
1327 }
1328 cnt = 0;
1329 stop = end_line(stop); // get to end of this line
1330 for (q = start; q <= stop && q <= end - 1; q++) {
1331 if (*q == '\n')
1332 cnt++;
1333 }
1334 return (cnt);
1335}
1336
1337static Byte *find_line(int li) // find begining of line #li
1338{
1339 Byte *q;
1340
1341 for (q = text; li > 1; li--) {
1342 q = next_line(q);
1343 }
1344 return (q);
1345}
1346
1347//----- Dot Movement Routines ----------------------------------
1348static void dot_left(void)
1349{
1350 if (dot > text && dot[-1] != '\n')
1351 dot--;
1352}
1353
1354static void dot_right(void)
1355{
1356 if (dot < end - 1 && *dot != '\n')
1357 dot++;
1358}
1359
1360static void dot_begin(void)
1361{
1362 dot = begin_line(dot); // return pointer to first char cur line
1363}
1364
1365static void dot_end(void)
1366{
1367 dot = end_line(dot); // return pointer to last char cur line
1368}
1369
1370static Byte *move_to_col(Byte * p, int l)
1371{
1372 int co;
1373
1374 p = begin_line(p);
1375 co = 0;
1376 do {
1377 if (*p == '\n' || *p == '\0')
1378 break;
1379 if (*p == '\t') {
1380 // 7 - (co % 8 )
1381 co += ((tabstop - 1) - (co % tabstop));
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001382 } else if (*p < ' ' || *p == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001383 co++; // display as ^X, use 2 columns
1384 }
1385 } while (++co <= l && p++ < end);
1386 return (p);
1387}
1388
1389static void dot_next(void)
1390{
1391 dot = next_line(dot);
1392}
1393
1394static void dot_prev(void)
1395{
1396 dot = prev_line(dot);
1397}
1398
1399static void dot_scroll(int cnt, int dir)
1400{
1401 Byte *q;
1402
1403 for (; cnt > 0; cnt--) {
1404 if (dir < 0) {
1405 // scroll Backwards
1406 // ctrl-Y scroll up one line
1407 screenbegin = prev_line(screenbegin);
1408 } else {
1409 // scroll Forwards
1410 // ctrl-E scroll down one line
1411 screenbegin = next_line(screenbegin);
1412 }
1413 }
1414 // make sure "dot" stays on the screen so we dont scroll off
1415 if (dot < screenbegin)
1416 dot = screenbegin;
1417 q = end_screen(); // find new bottom line
1418 if (dot > q)
1419 dot = begin_line(q); // is dot is below bottom line?
1420 dot_skip_over_ws();
1421}
1422
1423static void dot_skip_over_ws(void)
1424{
1425 // skip WS
1426 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1427 dot++;
1428}
1429
1430static void dot_delete(void) // delete the char at 'dot'
1431{
1432 (void) text_hole_delete(dot, dot);
1433}
1434
1435static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1436{
1437 if (p >= end && end > text) {
1438 p = end - 1;
1439 indicate_error('1');
1440 }
1441 if (p < text) {
1442 p = text;
1443 indicate_error('2');
1444 }
1445 return (p);
1446}
1447
1448//----- Helper Utility Routines --------------------------------
1449
1450//----------------------------------------------------------------
1451//----- Char Routines --------------------------------------------
1452/* Chars that are part of a word-
1453 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1454 * Chars that are Not part of a word (stoppers)
1455 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1456 * Chars that are WhiteSpace
1457 * TAB NEWLINE VT FF RETURN SPACE
1458 * DO NOT COUNT NEWLINE AS WHITESPACE
1459 */
1460
1461static Byte *new_screen(int ro, int co)
1462{
1463 int li;
1464
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001465 free(screen);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001466 screensize = ro * co + 8;
1467 screen = (Byte *) xmalloc(screensize);
1468 // initialize the new screen. assume this will be a empty file.
1469 screen_erase();
Eric Andersenaff114c2004-04-14 17:51:38 +00001470 // non-existent text[] lines start with a tilde (~).
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001471 for (li = 1; li < ro - 1; li++) {
1472 screen[(li * co) + 0] = '~';
1473 }
1474 return (screen);
1475}
1476
1477static Byte *new_text(int size)
1478{
1479 if (size < 10240)
1480 size = 10240; // have a minimum size for new files
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001481 free(text);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001482 text = (Byte *) xmalloc(size + 8);
1483 memset(text, '\0', size); // clear new text[]
1484 //text += 4; // leave some room for "oops"
1485 textend = text + size - 1;
1486 //textend -= 4; // leave some root for "oops"
1487 return (text);
1488}
1489
1490#ifdef CONFIG_FEATURE_VI_SEARCH
1491static int mycmp(Byte * s1, Byte * s2, int len)
1492{
1493 int i;
1494
1495 i = strncmp((char *) s1, (char *) s2, len);
1496#ifdef CONFIG_FEATURE_VI_SETOPTS
1497 if (ignorecase) {
1498 i = strncasecmp((char *) s1, (char *) s2, len);
1499 }
1500#endif /* CONFIG_FEATURE_VI_SETOPTS */
1501 return (i);
1502}
1503
1504static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1505{
1506#ifndef REGEX_SEARCH
1507 Byte *start, *stop;
1508 int len;
1509
1510 len = strlen((char *) pat);
1511 if (dir == FORWARD) {
1512 stop = end - 1; // assume range is p - end-1
1513 if (range == LIMITED)
1514 stop = next_line(p); // range is to next line
1515 for (start = p; start < stop; start++) {
1516 if (mycmp(start, pat, len) == 0) {
1517 return (start);
1518 }
1519 }
1520 } else if (dir == BACK) {
1521 stop = text; // assume range is text - p
1522 if (range == LIMITED)
1523 stop = prev_line(p); // range is to prev line
1524 for (start = p - len; start >= stop; start--) {
1525 if (mycmp(start, pat, len) == 0) {
1526 return (start);
1527 }
1528 }
1529 }
1530 // pattern not found
1531 return (NULL);
1532#else /*REGEX_SEARCH */
1533 char *q;
1534 struct re_pattern_buffer preg;
1535 int i;
1536 int size, range;
1537
1538 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1539 preg.translate = 0;
1540 preg.fastmap = 0;
1541 preg.buffer = 0;
1542 preg.allocated = 0;
1543
1544 // assume a LIMITED forward search
1545 q = next_line(p);
1546 q = end_line(q);
1547 q = end - 1;
1548 if (dir == BACK) {
1549 q = prev_line(p);
1550 q = text;
1551 }
1552 // count the number of chars to search over, forward or backward
1553 size = q - p;
1554 if (size < 0)
1555 size = p - q;
1556 // RANGE could be negative if we are searching backwards
1557 range = q - p;
1558
1559 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1560 if (q != 0) {
1561 // The pattern was not compiled
1562 psbs("bad search pattern: \"%s\": %s", pat, q);
1563 i = 0; // return p if pattern not compiled
1564 goto cs1;
1565 }
1566
1567 q = p;
1568 if (range < 0) {
1569 q = p - size;
1570 if (q < text)
1571 q = text;
1572 }
1573 // search for the compiled pattern, preg, in p[]
1574 // range < 0- search backward
1575 // range > 0- search forward
1576 // 0 < start < size
1577 // re_search() < 0 not found or error
1578 // re_search() > 0 index of found pattern
1579 // struct pattern char int int int struct reg
1580 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1581 i = re_search(&preg, q, size, 0, range, 0);
1582 if (i == -1) {
1583 p = 0;
1584 i = 0; // return NULL if pattern not found
1585 }
1586 cs1:
1587 if (dir == FORWARD) {
1588 p = p + i;
1589 } else {
1590 p = p - i;
1591 }
1592 return (p);
1593#endif /*REGEX_SEARCH */
1594}
1595#endif /* CONFIG_FEATURE_VI_SEARCH */
1596
1597static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1598{
1599 if (c == 22) { // Is this an ctrl-V?
1600 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1601 p--; // backup onto ^
1602 refresh(FALSE); // show the ^
1603 c = get_one_char();
1604 *p = c;
1605 p++;
Paul Fox8552aec2005-09-16 12:20:05 +00001606 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001607 } else if (c == 27) { // Is this an ESC?
1608 cmd_mode = 0;
1609 cmdcnt = 0;
1610 end_cmd_q(); // stop adding to q
Paul Fox8552aec2005-09-16 12:20:05 +00001611 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001612 if ((p[-1] != '\n') && (dot>text)) {
1613 p--;
1614 }
Paul Foxd13b90b2005-07-18 22:17:25 +00001615 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001616 // 123456789
1617 if ((p[-1] != '\n') && (dot>text)) {
1618 p--;
1619 p = text_hole_delete(p, p); // shrink buffer 1 char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001620 }
1621 } else {
1622 // insert a char into text[]
1623 Byte *sp; // "save p"
1624
1625 if (c == 13)
1626 c = '\n'; // translate \r to \n
1627 sp = p; // remember addr of insert
1628 p = stupid_insert(p, c); // insert the char
1629#ifdef CONFIG_FEATURE_VI_SETOPTS
1630 if (showmatch && strchr(")]}", *sp) != NULL) {
1631 showmatching(sp);
1632 }
1633 if (autoindent && c == '\n') { // auto indent the new line
1634 Byte *q;
1635
1636 q = prev_line(p); // use prev line as templet
1637 for (; isblnk(*q); q++) {
1638 p = stupid_insert(p, *q); // insert the char
1639 }
1640 }
1641#endif /* CONFIG_FEATURE_VI_SETOPTS */
1642 }
1643 return (p);
1644}
1645
1646static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1647{
1648 p = text_hole_make(p, 1);
1649 if (p != 0) {
1650 *p = c;
Paul Fox8552aec2005-09-16 12:20:05 +00001651 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001652 p++;
1653 }
1654 return (p);
1655}
1656
1657static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1658{
1659 Byte *save_dot, *p, *q;
1660 int cnt;
1661
1662 save_dot = dot;
1663 p = q = dot;
1664
1665 if (strchr("cdy><", c)) {
1666 // these cmds operate on whole lines
1667 p = q = begin_line(p);
1668 for (cnt = 1; cnt < cmdcnt; cnt++) {
1669 q = next_line(q);
1670 }
1671 q = end_line(q);
1672 } else if (strchr("^%$0bBeEft", c)) {
1673 // These cmds operate on char positions
1674 do_cmd(c); // execute movement cmd
1675 q = dot;
1676 } else if (strchr("wW", c)) {
1677 do_cmd(c); // execute movement cmd
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001678 // if we are at the next word's first char
1679 // step back one char
1680 // but check the possibilities when it is true
Eric Andersen5cc90ea2004-02-06 10:36:08 +00001681 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00001682 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1683 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1684 dot--; // move back off of next word
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001685 if (dot > text && *dot == '\n')
1686 dot--; // stay off NL
1687 q = dot;
1688 } else if (strchr("H-k{", c)) {
1689 // these operate on multi-lines backwards
1690 q = end_line(dot); // find NL
1691 do_cmd(c); // execute movement cmd
1692 dot_begin();
1693 p = dot;
1694 } else if (strchr("L+j}\r\n", c)) {
1695 // these operate on multi-lines forwards
1696 p = begin_line(dot);
1697 do_cmd(c); // execute movement cmd
1698 dot_end(); // find NL
1699 q = dot;
1700 } else {
1701 c = 27; // error- return an ESC char
1702 //break;
1703 }
1704 *start = p;
1705 *stop = q;
1706 if (q < p) {
1707 *start = q;
1708 *stop = p;
1709 }
1710 dot = save_dot;
1711 return (c);
1712}
1713
1714static int st_test(Byte * p, int type, int dir, Byte * tested)
1715{
1716 Byte c, c0, ci;
1717 int test, inc;
1718
1719 inc = dir;
1720 c = c0 = p[0];
1721 ci = p[inc];
1722 test = 0;
1723
1724 if (type == S_BEFORE_WS) {
1725 c = ci;
1726 test = ((!isspace(c)) || c == '\n');
1727 }
1728 if (type == S_TO_WS) {
1729 c = c0;
1730 test = ((!isspace(c)) || c == '\n');
1731 }
1732 if (type == S_OVER_WS) {
1733 c = c0;
1734 test = ((isspace(c)));
1735 }
1736 if (type == S_END_PUNCT) {
1737 c = ci;
1738 test = ((ispunct(c)));
1739 }
1740 if (type == S_END_ALNUM) {
1741 c = ci;
1742 test = ((isalnum(c)) || c == '_');
1743 }
1744 *tested = c;
1745 return (test);
1746}
1747
1748static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1749{
1750 Byte c;
1751
1752 while (st_test(p, type, dir, &c)) {
1753 // make sure we limit search to correct number of lines
1754 if (c == '\n' && --linecnt < 1)
1755 break;
1756 if (dir >= 0 && p >= end - 1)
1757 break;
1758 if (dir < 0 && p <= text)
1759 break;
1760 p += dir; // move to next char
1761 }
1762 return (p);
1763}
1764
1765// find matching char of pair () [] {}
1766static Byte *find_pair(Byte * p, Byte c)
1767{
1768 Byte match, *q;
1769 int dir, level;
1770
1771 match = ')';
1772 level = 1;
1773 dir = 1; // assume forward
1774 switch (c) {
1775 case '(':
1776 match = ')';
1777 break;
1778 case '[':
1779 match = ']';
1780 break;
1781 case '{':
1782 match = '}';
1783 break;
1784 case ')':
1785 match = '(';
1786 dir = -1;
1787 break;
1788 case ']':
1789 match = '[';
1790 dir = -1;
1791 break;
1792 case '}':
1793 match = '{';
1794 dir = -1;
1795 break;
1796 }
1797 for (q = p + dir; text <= q && q < end; q += dir) {
1798 // look for match, count levels of pairs (( ))
1799 if (*q == c)
1800 level++; // increase pair levels
1801 if (*q == match)
1802 level--; // reduce pair level
1803 if (level == 0)
1804 break; // found matching pair
1805 }
1806 if (level != 0)
1807 q = NULL; // indicate no match
1808 return (q);
1809}
1810
1811#ifdef CONFIG_FEATURE_VI_SETOPTS
1812// show the matching char of a pair, () [] {}
1813static void showmatching(Byte * p)
1814{
1815 Byte *q, *save_dot;
1816
1817 // we found half of a pair
1818 q = find_pair(p, *p); // get loc of matching char
1819 if (q == NULL) {
1820 indicate_error('3'); // no matching char
1821 } else {
1822 // "q" now points to matching pair
1823 save_dot = dot; // remember where we are
1824 dot = q; // go to new loc
1825 refresh(FALSE); // let the user see it
1826 (void) mysleep(40); // give user some time
1827 dot = save_dot; // go back to old loc
1828 refresh(FALSE);
1829 }
1830}
1831#endif /* CONFIG_FEATURE_VI_SETOPTS */
1832
1833// open a hole in text[]
1834static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1835{
1836 Byte *src, *dest;
1837 int cnt;
1838
1839 if (size <= 0)
1840 goto thm0;
1841 src = p;
1842 dest = p + size;
1843 cnt = end - src; // the rest of buffer
1844 if (memmove(dest, src, cnt) != dest) {
1845 psbs("can't create room for new characters");
1846 }
1847 memset(p, ' ', size); // clear new hole
1848 end = end + size; // adjust the new END
Paul Fox8552aec2005-09-16 12:20:05 +00001849 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001850 thm0:
1851 return (p);
1852}
1853
1854// close a hole in text[]
1855static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1856{
1857 Byte *src, *dest;
1858 int cnt, hole_size;
1859
1860 // move forwards, from beginning
1861 // assume p <= q
1862 src = q + 1;
1863 dest = p;
1864 if (q < p) { // they are backward- swap them
1865 src = p + 1;
1866 dest = q;
1867 }
1868 hole_size = q - p + 1;
1869 cnt = end - src;
1870 if (src < text || src > end)
1871 goto thd0;
1872 if (dest < text || dest >= end)
1873 goto thd0;
1874 if (src >= end)
1875 goto thd_atend; // just delete the end of the buffer
1876 if (memmove(dest, src, cnt) != dest) {
1877 psbs("can't delete the character");
1878 }
1879 thd_atend:
1880 end = end - hole_size; // adjust the new END
1881 if (dest >= end)
1882 dest = end - 1; // make sure dest in below end-1
1883 if (end <= text)
1884 dest = end = text; // keep pointers valid
Paul Fox8552aec2005-09-16 12:20:05 +00001885 file_modified++; // has the file been modified
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001886 thd0:
1887 return (dest);
1888}
1889
1890// copy text into register, then delete text.
1891// if dist <= 0, do not include, or go past, a NewLine
1892//
1893static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1894{
1895 Byte *p;
1896
1897 // make sure start <= stop
1898 if (start > stop) {
1899 // they are backwards, reverse them
1900 p = start;
1901 start = stop;
1902 stop = p;
1903 }
1904 if (dist <= 0) {
1905 // we can not cross NL boundaries
1906 p = start;
1907 if (*p == '\n')
1908 return (p);
1909 // dont go past a NewLine
1910 for (; p + 1 <= stop; p++) {
1911 if (p[1] == '\n') {
1912 stop = p; // "stop" just before NewLine
1913 break;
1914 }
1915 }
1916 }
1917 p = start;
1918#ifdef CONFIG_FEATURE_VI_YANKMARK
1919 text_yank(start, stop, YDreg);
1920#endif /* CONFIG_FEATURE_VI_YANKMARK */
1921 if (yf == YANKDEL) {
1922 p = text_hole_delete(start, stop);
1923 } // delete lines
1924 return (p);
1925}
1926
1927static void show_help(void)
1928{
1929 puts("These features are available:"
1930#ifdef CONFIG_FEATURE_VI_SEARCH
1931 "\n\tPattern searches with / and ?"
1932#endif /* CONFIG_FEATURE_VI_SEARCH */
1933#ifdef CONFIG_FEATURE_VI_DOT_CMD
1934 "\n\tLast command repeat with \'.\'"
1935#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1936#ifdef CONFIG_FEATURE_VI_YANKMARK
1937 "\n\tLine marking with 'x"
1938 "\n\tNamed buffers with \"x"
1939#endif /* CONFIG_FEATURE_VI_YANKMARK */
1940#ifdef CONFIG_FEATURE_VI_READONLY
1941 "\n\tReadonly if vi is called as \"view\""
1942 "\n\tReadonly with -R command line arg"
1943#endif /* CONFIG_FEATURE_VI_READONLY */
1944#ifdef CONFIG_FEATURE_VI_SET
1945 "\n\tSome colon mode commands with \':\'"
1946#endif /* CONFIG_FEATURE_VI_SET */
1947#ifdef CONFIG_FEATURE_VI_SETOPTS
1948 "\n\tSettable options with \":set\""
1949#endif /* CONFIG_FEATURE_VI_SETOPTS */
1950#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1951 "\n\tSignal catching- ^C"
1952 "\n\tJob suspend and resume with ^Z"
1953#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1954#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1955 "\n\tAdapt to window re-sizes"
1956#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1957 );
1958}
1959
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001960static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001961{
1962 Byte c, b[2];
1963
1964 b[1] = '\0';
1965 strcpy((char *) buf, ""); // init buf
1966 if (strlen((char *) s) <= 0)
1967 s = (Byte *) "(NULL)";
1968 for (; *s > '\0'; s++) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001969 int c_is_no_print;
1970
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001971 c = *s;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001972 c_is_no_print = c > 127 && !Isprint(c);
1973 if (c_is_no_print) {
1974 strcat((char *) buf, SOn);
1975 c = '.';
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001976 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001977 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001978 strcat((char *) buf, "^");
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001979 if(c == 127)
1980 c = '?';
1981 else
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001982 c += '@';
1983 }
1984 b[0] = c;
1985 strcat((char *) buf, (char *) b);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00001986 if (c_is_no_print)
1987 strcat((char *) buf, SOs);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001988 if (*s == '\n') {
1989 strcat((char *) buf, "$");
1990 }
1991 }
1992}
1993
1994#ifdef CONFIG_FEATURE_VI_DOT_CMD
1995static void start_new_cmd_q(Byte c)
1996{
1997 // release old cmd
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00001998 free(last_modifying_cmd);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00001999 // get buffer for new cmd
2000 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
2001 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
2002 // if there is a current cmd count put it in the buffer first
2003 if (cmdcnt > 0)
Paul Foxd957b952005-11-28 18:07:53 +00002004 sprintf((char *) last_modifying_cmd, "%d%c", cmdcnt, c);
2005 else // just save char c onto queue
2006 last_modifying_cmd[0] = c;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002007 adding2q = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002008}
2009
2010static void end_cmd_q(void)
2011{
2012#ifdef CONFIG_FEATURE_VI_YANKMARK
2013 YDreg = 26; // go back to default Yank/Delete reg
2014#endif /* CONFIG_FEATURE_VI_YANKMARK */
2015 adding2q = 0;
2016 return;
2017}
2018#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2019
2020#if defined(CONFIG_FEATURE_VI_YANKMARK) || defined(CONFIG_FEATURE_VI_COLON) || defined(CONFIG_FEATURE_VI_CRASHME)
2021static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
2022{
2023 int cnt, i;
2024
2025 i = strlen((char *) s);
2026 p = text_hole_make(p, i);
2027 strncpy((char *) p, (char *) s, i);
2028 for (cnt = 0; *s != '\0'; s++) {
2029 if (*s == '\n')
2030 cnt++;
2031 }
2032#ifdef CONFIG_FEATURE_VI_YANKMARK
2033 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
2034#endif /* CONFIG_FEATURE_VI_YANKMARK */
2035 return (p);
2036}
2037#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
2038
2039#ifdef CONFIG_FEATURE_VI_YANKMARK
2040static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
2041{
2042 Byte *t;
2043 int cnt;
2044
2045 if (q < p) { // they are backwards- reverse them
2046 t = q;
2047 q = p;
2048 p = t;
2049 }
2050 cnt = q - p + 1;
2051 t = reg[dest];
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002052 free(t); // if already a yank register, free it
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002053 t = (Byte *) xmalloc(cnt + 1); // get a new register
2054 memset(t, '\0', cnt + 1); // clear new text[]
2055 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
2056 reg[dest] = t;
2057 return (p);
2058}
2059
2060static Byte what_reg(void)
2061{
2062 Byte c;
2063 int i;
2064
2065 i = 0;
2066 c = 'D'; // default to D-reg
2067 if (0 <= YDreg && YDreg <= 25)
2068 c = 'a' + (Byte) YDreg;
2069 if (YDreg == 26)
2070 c = 'D';
2071 if (YDreg == 27)
2072 c = 'U';
2073 return (c);
2074}
2075
2076static void check_context(Byte cmd)
2077{
2078 // A context is defined to be "modifying text"
2079 // Any modifying command establishes a new context.
2080
2081 if (dot < context_start || dot > context_end) {
2082 if (strchr((char *) modifying_cmds, cmd) != NULL) {
2083 // we are trying to modify text[]- make this the current context
2084 mark[27] = mark[26]; // move cur to prev
2085 mark[26] = dot; // move local to cur
2086 context_start = prev_line(prev_line(dot));
2087 context_end = next_line(next_line(dot));
2088 //loiter= start_loiter= now;
2089 }
2090 }
2091 return;
2092}
2093
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002094static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002095{
2096 Byte *tmp;
2097
2098 // the current context is in mark[26]
2099 // the previous context is in mark[27]
2100 // only swap context if other context is valid
2101 if (text <= mark[27] && mark[27] <= end - 1) {
2102 tmp = mark[27];
2103 mark[27] = mark[26];
2104 mark[26] = tmp;
2105 p = mark[26]; // where we are going- previous context
2106 context_start = prev_line(prev_line(prev_line(p)));
2107 context_end = next_line(next_line(next_line(p)));
2108 }
2109 return (p);
2110}
2111#endif /* CONFIG_FEATURE_VI_YANKMARK */
2112
2113static int isblnk(Byte c) // is the char a blank or tab
2114{
2115 return (c == ' ' || c == '\t');
2116}
2117
2118//----- Set terminal attributes --------------------------------
2119static void rawmode(void)
2120{
2121 tcgetattr(0, &term_orig);
2122 term_vi = term_orig;
2123 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2124 term_vi.c_iflag &= (~IXON & ~ICRNL);
2125 term_vi.c_oflag &= (~ONLCR);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002126 term_vi.c_cc[VMIN] = 1;
2127 term_vi.c_cc[VTIME] = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002128 erase_char = term_vi.c_cc[VERASE];
2129 tcsetattr(0, TCSANOW, &term_vi);
2130}
2131
2132static void cookmode(void)
2133{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002134 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002135 tcsetattr(0, TCSANOW, &term_orig);
2136}
2137
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002138//----- Come here when we get a window resize signal ---------
2139#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002140static void winch_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002141{
2142 signal(SIGWINCH, winch_sig);
2143#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2144 window_size_get(0);
2145#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2146 new_screen(rows, columns); // get memory for virtual screen
2147 redraw(TRUE); // re-draw the screen
2148}
2149
2150//----- Come here when we get a continue signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002151static void cont_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002152{
2153 rawmode(); // terminal to "raw"
Paul Fox8552aec2005-09-16 12:20:05 +00002154 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002155 redraw(TRUE); // re-draw the screen
2156
2157 signal(SIGTSTP, suspend_sig);
2158 signal(SIGCONT, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002159 kill(my_pid, SIGCONT);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002160}
2161
2162//----- Come here when we get a Suspend signal -------------------
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002163static void suspend_sig(int sig ATTRIBUTE_UNUSED)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002164{
2165 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2166 clear_to_eol(); // Erase to end of line
2167 cookmode(); // terminal to "cooked"
2168
2169 signal(SIGCONT, cont_sig);
2170 signal(SIGTSTP, SIG_DFL);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002171 kill(my_pid, SIGTSTP);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002172}
2173
2174//----- Come here when we get a signal ---------------------------
2175static void catch_sig(int sig)
2176{
2177 signal(SIGHUP, catch_sig);
2178 signal(SIGINT, catch_sig);
2179 signal(SIGTERM, catch_sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002180 signal(SIGALRM, catch_sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002181 if(sig)
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002182 longjmp(restart, sig);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002183}
2184
2185//----- Come here when we get a core dump signal -----------------
2186static void core_sig(int sig)
2187{
2188 signal(SIGQUIT, core_sig);
2189 signal(SIGILL, core_sig);
2190 signal(SIGTRAP, core_sig);
2191 signal(SIGIOT, core_sig);
2192 signal(SIGABRT, core_sig);
2193 signal(SIGFPE, core_sig);
2194 signal(SIGBUS, core_sig);
2195 signal(SIGSEGV, core_sig);
2196#ifdef SIGSYS
2197 signal(SIGSYS, core_sig);
2198#endif
2199
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002200 if(sig) { // signaled
"Vladimir N. Oleynik"cd473dd2006-01-30 13:41:53 +00002201 dot = bound_dot(dot); // make sure "dot" is valid
2202 longjmp(restart, sig);
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002203 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002204}
2205#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2206
2207static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2208{
2209 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002210 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002211 FD_ZERO(&rfds);
2212 FD_SET(0, &rfds);
2213 tv.tv_sec = 0;
2214 tv.tv_usec = hund * 10000;
2215 select(1, &rfds, NULL, NULL, &tv);
2216 return (FD_ISSET(0, &rfds));
2217}
2218
"Vladimir N. Oleynik"6f347ef2005-10-15 10:23:55 +00002219#define readbuffer bb_common_bufsiz1
2220
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002221static int readed_for_parse;
2222
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002223//----- IO Routines --------------------------------------------
2224static Byte readit(void) // read (maybe cursor) key from stdin
2225{
2226 Byte c;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002227 int n;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002228 struct esc_cmds {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002229 const char *seq;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002230 Byte val;
2231 };
2232
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002233 static const struct esc_cmds esccmds[] = {
2234 {"OA", (Byte) VI_K_UP}, // cursor key Up
2235 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2236 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2237 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2238 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2239 {"OF", (Byte) VI_K_END}, // Cursor Key End
2240 {"[A", (Byte) VI_K_UP}, // cursor key Up
2241 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2242 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2243 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2244 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2245 {"[F", (Byte) VI_K_END}, // Cursor Key End
2246 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2247 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2248 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2249 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2250 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2251 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2252 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2253 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2254 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2255 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2256 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2257 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2258 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2259 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2260 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2261 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2262 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2263 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2264 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2265 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2266 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002267 };
2268
2269#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2270
2271 (void) alarm(0); // turn alarm OFF while we wait for input
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002272 fflush(stdout);
2273 n = readed_for_parse;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002274 // get input from User- are there already input chars in Q?
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002275 if (n <= 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002276 ri0:
2277 // the Q is empty, wait for a typed char
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002278 n = read(0, readbuffer, BUFSIZ - 1);
2279 if (n < 0) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002280 if (errno == EINTR)
2281 goto ri0; // interrupted sys call
2282 if (errno == EBADF)
2283 editing = 0;
2284 if (errno == EFAULT)
2285 editing = 0;
2286 if (errno == EINVAL)
2287 editing = 0;
2288 if (errno == EIO)
2289 editing = 0;
2290 errno = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002291 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002292 if(n <= 0)
2293 return 0; // error
2294 if (readbuffer[0] == 27) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002295 // This is an ESC char. Is this Esc sequence?
2296 // Could be bare Esc key. See if there are any
2297 // more chars to read after the ESC. This would
2298 // be a Function or Cursor Key sequence.
2299 FD_ZERO(&rfds);
2300 FD_SET(0, &rfds);
2301 tv.tv_sec = 0;
2302 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2303
2304 // keep reading while there are input chars and room in buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002305 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002306 // read the rest of the ESC string
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002307 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2308 if (r > 0) {
2309 n += r;
2310 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002311 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002312 }
2313 readed_for_parse = n;
2314 }
2315 c = readbuffer[0];
2316 if(c == 27 && n > 1) {
2317 // Maybe cursor or function key?
2318 const struct esc_cmds *eindex;
2319
2320 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2321 int cnt = strlen(eindex->seq);
2322
2323 if(n <= cnt)
2324 continue;
2325 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2326 continue;
2327 // is a Cursor key- put derived value back into Q
2328 c = eindex->val;
2329 // for squeeze out the ESC sequence
2330 n = cnt + 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002331 break;
2332 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002333 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2334 /* defined ESC sequence not found, set only one ESC */
2335 n = 1;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002336 }
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002337 } else {
2338 n = 1;
2339 }
2340 // remove key sequence from Q
2341 readed_for_parse -= n;
2342 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002343 (void) alarm(3); // we are done waiting for input, turn alarm ON
2344 return (c);
2345}
2346
2347//----- IO Routines --------------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002348static Byte get_one_char(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002349{
2350 static Byte c;
2351
2352#ifdef CONFIG_FEATURE_VI_DOT_CMD
2353 // ! adding2q && ioq == 0 read()
2354 // ! adding2q && ioq != 0 *ioq
2355 // adding2q *last_modifying_cmd= read()
2356 if (!adding2q) {
2357 // we are not adding to the q.
2358 // but, we may be reading from a q
2359 if (ioq == 0) {
2360 // there is no current q, read from STDIN
2361 c = readit(); // get the users input
2362 } else {
2363 // there is a queue to get chars from first
2364 c = *ioq++;
2365 if (c == '\0') {
2366 // the end of the q, read from STDIN
2367 free(ioq_start);
2368 ioq_start = ioq = 0;
2369 c = readit(); // get the users input
2370 }
2371 }
2372 } else {
2373 // adding STDIN chars to q
2374 c = readit(); // get the users input
2375 if (last_modifying_cmd != 0) {
Eric Andersenfda2b7f2002-10-26 10:19:19 +00002376 int len = strlen((char *) last_modifying_cmd);
2377 if (len + 1 >= BUFSIZ) {
2378 psbs("last_modifying_cmd overrun");
2379 } else {
2380 // add new char to q
2381 last_modifying_cmd[len] = c;
2382 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002383 }
2384 }
2385#else /* CONFIG_FEATURE_VI_DOT_CMD */
2386 c = readit(); // get the users input
2387#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2388 return (c); // return the char, where ever it came from
2389}
2390
2391static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2392{
2393 Byte buf[BUFSIZ];
2394 Byte c;
2395 int i;
2396 static Byte *obufp = NULL;
2397
2398 strcpy((char *) buf, (char *) prompt);
Paul Fox8552aec2005-09-16 12:20:05 +00002399 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002400 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2401 clear_to_eol(); // clear the line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002402 write1((char *) prompt); // write out the :, /, or ? prompt
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002403
2404 for (i = strlen((char *) buf); i < BUFSIZ;) {
2405 c = get_one_char(); // read user input
2406 if (c == '\n' || c == '\r' || c == 27)
2407 break; // is this end of input
Paul Foxf2de0b72005-09-13 22:20:37 +00002408 if (c == erase_char || c == 8 || c == 127) {
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00002409 // user wants to erase prev char
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002410 i--; // backup to prev char
2411 buf[i] = '\0'; // erase the char
2412 buf[i + 1] = '\0'; // null terminate buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002413 write1("\b \b"); // erase char on screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002414 if (i <= 0) { // user backs up before b-o-l, exit
2415 break;
2416 }
2417 } else {
2418 buf[i] = c; // save char in buffer
2419 buf[i + 1] = '\0'; // make sure buffer is null terminated
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002420 putchar(c); // echo the char back to user
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002421 i++;
2422 }
2423 }
2424 refresh(FALSE);
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00002425 free(obufp);
Manuel Novoa III cad53642003-03-19 09:13:01 +00002426 obufp = (Byte *) bb_xstrdup((char *) buf);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002427 return (obufp);
2428}
2429
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002430static int file_size(const Byte * fn) // what is the byte size of "fn"
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002431{
2432 struct stat st_buf;
2433 int cnt, sr;
2434
Eric Andersena68ea1c2006-01-30 22:48:39 +00002435 if (fn == 0 || strlen((char *)fn) <= 0)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002436 return (-1);
2437 cnt = -1;
2438 sr = stat((char *) fn, &st_buf); // see if file exists
2439 if (sr >= 0) {
2440 cnt = (int) st_buf.st_size;
2441 }
2442 return (cnt);
2443}
2444
2445static int file_insert(Byte * fn, Byte * p, int size)
2446{
2447 int fd, cnt;
2448
2449 cnt = -1;
2450#ifdef CONFIG_FEATURE_VI_READONLY
2451 readonly = FALSE;
2452#endif /* CONFIG_FEATURE_VI_READONLY */
2453 if (fn == 0 || strlen((char*) fn) <= 0) {
2454 psbs("No filename given");
2455 goto fi0;
2456 }
2457 if (size == 0) {
2458 // OK- this is just a no-op
2459 cnt = 0;
2460 goto fi0;
2461 }
2462 if (size < 0) {
2463 psbs("Trying to insert a negative number (%d) of characters", size);
2464 goto fi0;
2465 }
2466 if (p < text || p > end) {
2467 psbs("Trying to insert file outside of memory");
2468 goto fi0;
2469 }
2470
2471 // see if we can open the file
2472#ifdef CONFIG_FEATURE_VI_READONLY
2473 if (vi_readonly) goto fi1; // do not try write-mode
2474#endif
2475 fd = open((char *) fn, O_RDWR); // assume read & write
2476 if (fd < 0) {
2477 // could not open for writing- maybe file is read only
2478#ifdef CONFIG_FEATURE_VI_READONLY
2479 fi1:
2480#endif
2481 fd = open((char *) fn, O_RDONLY); // try read-only
2482 if (fd < 0) {
2483 psbs("\"%s\" %s", fn, "could not open file");
2484 goto fi0;
2485 }
2486#ifdef CONFIG_FEATURE_VI_READONLY
2487 // got the file- read-only
2488 readonly = TRUE;
2489#endif /* CONFIG_FEATURE_VI_READONLY */
2490 }
2491 p = text_hole_make(p, size);
2492 cnt = read(fd, p, size);
2493 close(fd);
2494 if (cnt < 0) {
2495 cnt = -1;
2496 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2497 psbs("could not read file \"%s\"", fn);
2498 } else if (cnt < size) {
2499 // There was a partial read, shrink unused space text[]
2500 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2501 psbs("could not read all of file \"%s\"", fn);
2502 }
2503 if (cnt >= size)
Paul Fox8552aec2005-09-16 12:20:05 +00002504 file_modified++;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002505 fi0:
2506 return (cnt);
2507}
2508
2509static int file_write(Byte * fn, Byte * first, Byte * last)
2510{
2511 int fd, cnt, charcnt;
2512
2513 if (fn == 0) {
2514 psbs("No current filename");
Paul Fox61e45db2005-10-09 14:43:22 +00002515 return (-2);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002516 }
2517 charcnt = 0;
2518 // FIXIT- use the correct umask()
2519 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2520 if (fd < 0)
2521 return (-1);
2522 cnt = last - first + 1;
2523 charcnt = write(fd, first, cnt);
2524 if (charcnt == cnt) {
2525 // good write
2526 //file_modified= FALSE; // the file has not been modified
2527 } else {
2528 charcnt = 0;
2529 }
2530 close(fd);
2531 return (charcnt);
2532}
2533
2534//----- Terminal Drawing ---------------------------------------
2535// The terminal is made up of 'rows' line of 'columns' columns.
Eric Andersenaff114c2004-04-14 17:51:38 +00002536// classically this would be 24 x 80.
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002537// screen coordinates
2538// 0,0 ... 0,79
2539// 1,0 ... 1,79
2540// . ... .
2541// . ... .
2542// 22,0 ... 22,79
2543// 23,0 ... 23,79 status line
2544//
2545
2546//----- Move the cursor to row x col (count from 0, not 1) -------
2547static void place_cursor(int row, int col, int opti)
2548{
2549 char cm1[BUFSIZ];
2550 char *cm;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002551#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2552 char cm2[BUFSIZ];
2553 Byte *screenp;
2554 // char cm3[BUFSIZ];
2555 int Rrow= last_row;
2556#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002557
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002558 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2559
2560 if (row < 0) row = 0;
2561 if (row >= rows) row = rows - 1;
2562 if (col < 0) col = 0;
2563 if (col >= columns) col = columns - 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002564
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002565 //----- 1. Try the standard terminal ESC sequence
2566 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2567 cm= cm1;
2568 if (! opti) goto pc0;
2569
2570#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2571 //----- find the minimum # of chars to move cursor -------------
2572 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2573 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002574
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002575 // move to the correct row
2576 while (row < Rrow) {
2577 // the cursor has to move up
2578 strcat(cm2, CMup);
2579 Rrow--;
2580 }
2581 while (row > Rrow) {
2582 // the cursor has to move down
2583 strcat(cm2, CMdown);
2584 Rrow++;
2585 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002586
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002587 // now move to the correct column
2588 strcat(cm2, "\r"); // start at col 0
2589 // just send out orignal source char to get to correct place
2590 screenp = &screen[row * columns]; // start of screen line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002591 strncat(cm2, (char* )screenp, col);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002592
2593 //----- 3. Try some other way of moving cursor
2594 //---------------------------------------------
2595
2596 // pick the shortest cursor motion to send out
2597 cm= cm1;
2598 if (strlen(cm2) < strlen(cm)) {
2599 cm= cm2;
2600 } /* else if (strlen(cm3) < strlen(cm)) {
2601 cm= cm3;
2602 } */
2603#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2604 pc0:
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002605 write1(cm); // move the cursor
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002606}
2607
2608//----- Erase from cursor to end of line -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002609static void clear_to_eol(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002610{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002611 write1(Ceol); // Erase from cursor to end of line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002612}
2613
2614//----- Erase from cursor to end of screen -----------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002615static void clear_to_eos(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002616{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002617 write1(Ceos); // Erase from cursor to end of screen
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002618}
2619
2620//----- Start standout mode ------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002621static void standout_start(void) // send "start reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002622{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002623 write1(SOs); // Start reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002624}
2625
2626//----- End standout mode --------------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002627static void standout_end(void) // send "end reverse video" sequence
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002628{
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002629 write1(SOn); // End reverse video mode
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002630}
2631
2632//----- Flash the screen --------------------------------------
2633static void flash(int h)
2634{
2635 standout_start(); // send "start reverse video" sequence
2636 redraw(TRUE);
2637 (void) mysleep(h);
2638 standout_end(); // send "end reverse video" sequence
2639 redraw(TRUE);
2640}
2641
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002642static void Indicate_Error(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002643{
2644#ifdef CONFIG_FEATURE_VI_CRASHME
2645 if (crashme > 0)
2646 return; // generate a random command
2647#endif /* CONFIG_FEATURE_VI_CRASHME */
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002648 if (!err_method) {
2649 write1(bell); // send out a bell character
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002650 } else {
2651 flash(10);
2652 }
2653}
2654
2655//----- Screen[] Routines --------------------------------------
2656//----- Erase the Screen[] memory ------------------------------
Mike Frysinger4e5936e2005-04-16 04:30:38 +00002657static void screen_erase(void)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002658{
2659 memset(screen, ' ', screensize); // clear new screen
2660}
2661
Eric Andersena68ea1c2006-01-30 22:48:39 +00002662static int bufsum(unsigned char *buf, int count)
Paul Fox8552aec2005-09-16 12:20:05 +00002663{
2664 int sum = 0;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002665 unsigned char *e = buf + count;
Paul Fox8552aec2005-09-16 12:20:05 +00002666 while (buf < e)
2667 sum += *buf++;
2668 return sum;
2669}
2670
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002671//----- Draw the status line at bottom of the screen -------------
2672static void show_status_line(void)
2673{
Paul Foxc3504852005-09-16 12:48:18 +00002674 int cnt = 0, cksum = 0;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002675
Paul Fox8552aec2005-09-16 12:20:05 +00002676 // either we already have an error or status message, or we
2677 // create one.
2678 if (!have_status_msg) {
2679 cnt = format_edit_status();
2680 cksum = bufsum(status_buffer, cnt);
2681 }
2682 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2683 last_status_cksum= cksum; // remember if we have seen this line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002684 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
Eric Andersena68ea1c2006-01-30 22:48:39 +00002685 write1((char*)status_buffer);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002686 clear_to_eol();
Paul Fox8552aec2005-09-16 12:20:05 +00002687 if (have_status_msg) {
Eric Andersena68ea1c2006-01-30 22:48:39 +00002688 if (((int)strlen((char*)status_buffer) - (have_status_msg - 1)) >
Paul Fox8552aec2005-09-16 12:20:05 +00002689 (columns - 1) ) {
2690 have_status_msg = 0;
2691 Hit_Return();
2692 }
2693 have_status_msg = 0;
2694 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002695 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2696 }
Eric Andersena9eb33d2004-08-19 19:15:06 +00002697 fflush(stdout);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002698}
2699
2700//----- format the status buffer, the bottom line of screen ------
Paul Fox8552aec2005-09-16 12:20:05 +00002701// format status buffer, with STANDOUT mode
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002702static void psbs(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002703{
2704 va_list args;
2705
2706 va_start(args, format);
2707 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
Eric Andersen0ef24c62005-07-18 10:32:59 +00002708 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002709 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2710 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002711
2712 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002713
2714 return;
2715}
2716
Paul Fox8552aec2005-09-16 12:20:05 +00002717// format status buffer
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002718static void psb(const char *format, ...)
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002719{
2720 va_list args;
2721
2722 va_start(args, format);
2723 vsprintf((char *) status_buffer, format, args);
2724 va_end(args);
Paul Fox8552aec2005-09-16 12:20:05 +00002725
2726 have_status_msg = 1;
2727
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002728 return;
2729}
2730
2731static void ni(Byte * s) // display messages
2732{
2733 Byte buf[BUFSIZ];
2734
2735 print_literal(buf, s);
2736 psbs("\'%s\' is not implemented", buf);
2737}
2738
Paul Fox8552aec2005-09-16 12:20:05 +00002739static int format_edit_status(void) // show file status on status line
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002740{
Paul Fox8552aec2005-09-16 12:20:05 +00002741 int cur, percent, ret, trunc_at;
2742 static int tot;
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002743
Paul Fox8552aec2005-09-16 12:20:05 +00002744 // file_modified is now a counter rather than a flag. this
2745 // helps reduce the amount of line counting we need to do.
2746 // (this will cause a mis-reporting of modified status
2747 // once every MAXINT editing operations.)
2748
2749 // it would be nice to do a similar optimization here -- if
2750 // we haven't done a motion that could have changed which line
2751 // we're on, then we shouldn't have to do this count_lines()
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002752 cur = count_lines(text, dot);
Paul Fox8552aec2005-09-16 12:20:05 +00002753
2754 // reduce counting -- the total lines can't have
2755 // changed if we haven't done any edits.
2756 if (file_modified != last_file_modified) {
2757 tot = cur + count_lines(dot, end - 1) - 1;
2758 last_file_modified = file_modified;
2759 }
2760
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002761 // current line percent
2762 // ------------- ~~ ----------
2763 // total lines 100
2764 if (tot > 0) {
2765 percent = (100 * cur) / tot;
2766 } else {
2767 cur = tot = 0;
2768 percent = 100;
2769 }
Eric Andersen0ef24c62005-07-18 10:32:59 +00002770
Paul Fox8552aec2005-09-16 12:20:05 +00002771 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2772 columns : STATUS_BUFFER_LEN-1;
2773
2774 ret = snprintf((char *) status_buffer, trunc_at+1,
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002775#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002776 "%c %s%s%s %d/%d %d%%",
2777#else
2778 "%c %s%s %d/%d %d%%",
2779#endif
2780 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2781 (cfn != 0 ? (char *) cfn : "No file"),
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002782#ifdef CONFIG_FEATURE_VI_READONLY
Paul Fox8552aec2005-09-16 12:20:05 +00002783 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2784#endif
2785 (file_modified ? " [modified]" : ""),
2786 cur, tot, percent);
2787
2788 if (ret >= 0 && ret < trunc_at)
2789 return ret; /* it all fit */
2790
2791 return trunc_at; /* had to truncate */
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002792}
2793
2794//----- Force refresh of all Lines -----------------------------
2795static void redraw(int full_screen)
2796{
2797 place_cursor(0, 0, FALSE); // put cursor in correct place
2798 clear_to_eos(); // tel terminal to erase display
2799 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00002800 last_status_cksum = 0; // force status update
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002801 refresh(full_screen); // this will redraw the entire display
Paul Fox8552aec2005-09-16 12:20:05 +00002802 show_status_line();
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002803}
2804
2805//----- Format a text[] line into a buffer ---------------------
2806static void format_line(Byte *dest, Byte *src, int li)
2807{
2808 int co;
2809 Byte c;
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002810
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002811 for (co= 0; co < MAX_SCR_COLS; co++) {
2812 c= ' '; // assume blank
2813 if (li > 0 && co == 0) {
2814 c = '~'; // not first line, assume Tilde
2815 }
2816 // are there chars in text[] and have we gone past the end
2817 if (text < end && src < end) {
2818 c = *src++;
2819 }
2820 if (c == '\n')
2821 break;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002822 if (c > 127 && !Isprint(c)) {
2823 c = '.';
2824 }
2825 if (c < ' ' || c == 127) {
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002826 if (c == '\t') {
2827 c = ' ';
2828 // co % 8 != 7
2829 for (; (co % tabstop) != (tabstop - 1); co++) {
2830 dest[co] = c;
2831 }
2832 } else {
2833 dest[co++] = '^';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002834 if(c == 127)
2835 c = '?';
2836 else
2837 c += '@'; // make it visible
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002838 }
2839 }
2840 // the co++ is done here so that the column will
2841 // not be overwritten when we blank-out the rest of line
2842 dest[co] = c;
2843 if (src >= end)
2844 break;
2845 }
2846}
2847
2848//----- Refresh the changed screen lines -----------------------
2849// Copy the source line from text[] into the buffer and note
2850// if the current screenline is different from the new buffer.
2851// If they differ then that line needs redrawing on the terminal.
2852//
2853static void refresh(int full_screen)
2854{
2855 static int old_offset;
2856 int li, changed;
2857 Byte buf[MAX_SCR_COLS];
2858 Byte *tp, *sp; // pointer into text[] and screen[]
2859#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2860 int last_li= -2; // last line that changed- for optimizing cursor movement
2861#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2862
2863#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
2864 window_size_get(0);
2865#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
2866 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2867 tp = screenbegin; // index into text[] of top line
2868
2869 // compare text[] to screen[] and mark screen[] lines that need updating
2870 for (li = 0; li < rows - 1; li++) {
2871 int cs, ce; // column start & end
2872 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2873 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2874 // format current text line into buf
2875 format_line(buf, tp, li);
2876
2877 // skip to the end of the current text[] line
2878 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2879
2880 // see if there are any changes between vitual screen and buf
2881 changed = FALSE; // assume no change
2882 cs= 0;
2883 ce= columns-1;
2884 sp = &screen[li * columns]; // start of screen line
2885 if (full_screen) {
2886 // force re-draw of every single column from 0 - columns-1
2887 goto re0;
2888 }
2889 // compare newly formatted buffer with virtual screen
2890 // look forward for first difference between buf and screen
2891 for ( ; cs <= ce; cs++) {
2892 if (buf[cs + offset] != sp[cs]) {
2893 changed = TRUE; // mark for redraw
2894 break;
2895 }
2896 }
2897
2898 // look backward for last difference between buf and screen
2899 for ( ; ce >= cs; ce--) {
2900 if (buf[ce + offset] != sp[ce]) {
2901 changed = TRUE; // mark for redraw
2902 break;
2903 }
2904 }
2905 // now, cs is index of first diff, and ce is index of last diff
2906
2907 // if horz offset has changed, force a redraw
2908 if (offset != old_offset) {
2909 re0:
2910 changed = TRUE;
2911 }
2912
2913 // make a sanity check of columns indexes
2914 if (cs < 0) cs= 0;
2915 if (ce > columns-1) ce= columns-1;
2916 if (cs > ce) { cs= 0; ce= columns-1; }
2917 // is there a change between vitual screen and buf
2918 if (changed) {
2919 // copy changed part of buffer to virtual screen
2920 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2921
2922 // move cursor to column of first change
2923 if (offset != old_offset) {
2924 // opti_cur_move is still too stupid
2925 // to handle offsets correctly
2926 place_cursor(li, cs, FALSE);
2927 } else {
2928#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2929 // if this just the next line
2930 // try to optimize cursor movement
2931 // otherwise, use standard ESC sequence
2932 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2933 last_li= li;
2934#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2935 place_cursor(li, cs, FALSE); // use standard ESC sequence
2936#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2937 }
2938
2939 // write line out to terminal
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002940 {
2941 int nic = ce-cs+1;
Eric Andersena68ea1c2006-01-30 22:48:39 +00002942 char *out = (char*)sp+cs;
Glenn L McGrath09adaca2002-12-02 21:18:10 +00002943
2944 while(nic-- > 0) {
2945 putchar(*out);
2946 out++;
2947 }
2948 }
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002949#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2950 last_row = li;
2951#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2952 }
2953 }
2954
2955#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2956 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2957 last_row = crow;
2958#else
2959 place_cursor(crow, ccol, FALSE);
2960#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
Eric Andersenc7bda1c2004-03-15 08:29:22 +00002961
Aaron Lehmann6fdacc72002-08-21 13:02:24 +00002962 if (offset != old_offset)
2963 old_offset = offset;
2964}
2965
Eric Andersen3f980402001-04-04 17:31:15 +00002966//---------------------------------------------------------------------
2967//----- the Ascii Chart -----------------------------------------------
2968//
2969// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2970// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2971// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2972// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2973// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2974// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2975// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2976// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2977// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2978// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2979// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2980// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2981// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2982// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2983// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2984// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2985//---------------------------------------------------------------------
2986
2987//----- Execute a Vi Command -----------------------------------
2988static void do_cmd(Byte c)
2989{
2990 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2991 int cnt, i, j, dir, yf;
2992
2993 c1 = c; // quiet the compiler
2994 cnt = yf = dir = 0; // quiet the compiler
2995 p = q = save_dot = msg = buf; // quiet the compiler
2996 memset(buf, '\0', 9); // clear buf
Eric Andersenbff7a602001-11-17 07:15:43 +00002997
Paul Fox8552aec2005-09-16 12:20:05 +00002998 show_status_line();
2999
Eric Andersenbff7a602001-11-17 07:15:43 +00003000 /* if this is a cursor key, skip these checks */
3001 switch (c) {
3002 case VI_K_UP:
3003 case VI_K_DOWN:
3004 case VI_K_LEFT:
3005 case VI_K_RIGHT:
3006 case VI_K_HOME:
3007 case VI_K_END:
3008 case VI_K_PAGEUP:
3009 case VI_K_PAGEDOWN:
3010 goto key_cmd_mode;
3011 }
3012
Eric Andersen3f980402001-04-04 17:31:15 +00003013 if (cmd_mode == 2) {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003014 // flip-flop Insert/Replace mode
3015 if (c == VI_K_INSERT) goto dc_i;
Eric Andersen3f980402001-04-04 17:31:15 +00003016 // we are 'R'eplacing the current *dot with new char
3017 if (*dot == '\n') {
3018 // don't Replace past E-o-l
3019 cmd_mode = 1; // convert to insert
3020 } else {
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003021 if (1 <= c || Isprint(c)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003022 if (c != 27)
3023 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3024 dot = char_insert(dot, c); // insert new char
3025 }
3026 goto dc1;
3027 }
3028 }
3029 if (cmd_mode == 1) {
Eric Andersen1c0d3112001-04-16 15:46:44 +00003030 // hitting "Insert" twice means "R" replace mode
3031 if (c == VI_K_INSERT) goto dc5;
Eric Andersen3f980402001-04-04 17:31:15 +00003032 // insert the char c at "dot"
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003033 if (1 <= c || Isprint(c)) {
3034 dot = char_insert(dot, c);
Eric Andersen3f980402001-04-04 17:31:15 +00003035 }
3036 goto dc1;
3037 }
3038
Eric Andersenbff7a602001-11-17 07:15:43 +00003039key_cmd_mode:
Eric Andersen3f980402001-04-04 17:31:15 +00003040 switch (c) {
Eric Andersen822c3832001-05-07 17:37:43 +00003041 //case 0x01: // soh
3042 //case 0x09: // ht
3043 //case 0x0b: // vt
3044 //case 0x0e: // so
3045 //case 0x0f: // si
3046 //case 0x10: // dle
3047 //case 0x11: // dc1
3048 //case 0x13: // dc3
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003049#ifdef CONFIG_FEATURE_VI_CRASHME
Eric Andersen1c0d3112001-04-16 15:46:44 +00003050 case 0x14: // dc4 ctrl-T
Eric Andersen3f980402001-04-04 17:31:15 +00003051 crashme = (crashme == 0) ? 1 : 0;
Eric Andersen3f980402001-04-04 17:31:15 +00003052 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003053#endif /* CONFIG_FEATURE_VI_CRASHME */
Eric Andersen822c3832001-05-07 17:37:43 +00003054 //case 0x16: // syn
3055 //case 0x17: // etb
3056 //case 0x18: // can
3057 //case 0x1c: // fs
3058 //case 0x1d: // gs
3059 //case 0x1e: // rs
3060 //case 0x1f: // us
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003061 //case '!': // !-
3062 //case '#': // #-
3063 //case '&': // &-
3064 //case '(': // (-
3065 //case ')': // )-
3066 //case '*': // *-
3067 //case ',': // ,-
3068 //case '=': // =-
3069 //case '@': // @-
3070 //case 'F': // F-
3071 //case 'K': // K-
3072 //case 'Q': // Q-
3073 //case 'S': // S-
3074 //case 'T': // T-
3075 //case 'V': // V-
3076 //case '[': // [-
3077 //case '\\': // \-
3078 //case ']': // ]-
3079 //case '_': // _-
3080 //case '`': // `-
3081 //case 'g': // g-
Eric Andersen1c0d3112001-04-16 15:46:44 +00003082 //case 'u': // u- FIXME- there is no undo
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003083 //case 'v': // v-
Eric Andersen3f980402001-04-04 17:31:15 +00003084 default: // unrecognised command
3085 buf[0] = c;
3086 buf[1] = '\0';
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003087 if (c < ' ') {
Eric Andersen3f980402001-04-04 17:31:15 +00003088 buf[0] = '^';
3089 buf[1] = c + '@';
3090 buf[2] = '\0';
3091 }
3092 ni((Byte *) buf);
3093 end_cmd_q(); // stop adding to q
3094 case 0x00: // nul- ignore
3095 break;
3096 case 2: // ctrl-B scroll up full screen
3097 case VI_K_PAGEUP: // Cursor Key Page Up
3098 dot_scroll(rows - 2, -1);
3099 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003100#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
Eric Andersen3f980402001-04-04 17:31:15 +00003101 case 0x03: // ctrl-C interrupt
3102 longjmp(restart, 1);
3103 break;
3104 case 26: // ctrl-Z suspend
3105 suspend_sig(SIGTSTP);
3106 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003107#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
Eric Andersen3f980402001-04-04 17:31:15 +00003108 case 4: // ctrl-D scroll down half screen
3109 dot_scroll((rows - 2) / 2, 1);
3110 break;
3111 case 5: // ctrl-E scroll down one line
3112 dot_scroll(1, 1);
3113 break;
3114 case 6: // ctrl-F scroll down full screen
3115 case VI_K_PAGEDOWN: // Cursor Key Page Down
3116 dot_scroll(rows - 2, 1);
3117 break;
3118 case 7: // ctrl-G show current status
Paul Fox8552aec2005-09-16 12:20:05 +00003119 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003120 break;
3121 case 'h': // h- move left
3122 case VI_K_LEFT: // cursor key Left
Paul Foxd13b90b2005-07-18 22:17:25 +00003123 case 8: // ctrl-H- move left (This may be ERASE char)
3124 case 127: // DEL- move left (This may be ERASE char)
Eric Andersen3f980402001-04-04 17:31:15 +00003125 if (cmdcnt-- > 1) {
3126 do_cmd(c);
3127 } // repeat cnt
3128 dot_left();
3129 break;
3130 case 10: // Newline ^J
3131 case 'j': // j- goto next line, same col
3132 case VI_K_DOWN: // cursor key Down
3133 if (cmdcnt-- > 1) {
3134 do_cmd(c);
3135 } // repeat cnt
3136 dot_next(); // go to next B-o-l
3137 dot = move_to_col(dot, ccol + offset); // try stay in same col
3138 break;
3139 case 12: // ctrl-L force redraw whole screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003140 case 18: // ctrl-R force redraw
Eric Andersen822c3832001-05-07 17:37:43 +00003141 place_cursor(0, 0, FALSE); // put cursor in correct place
Eric Andersen3f980402001-04-04 17:31:15 +00003142 clear_to_eos(); // tel terminal to erase display
3143 (void) mysleep(10);
3144 screen_erase(); // erase the internal screen buffer
Paul Fox8552aec2005-09-16 12:20:05 +00003145 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003146 refresh(TRUE); // this will redraw the entire display
3147 break;
3148 case 13: // Carriage Return ^M
3149 case '+': // +- goto next line
3150 if (cmdcnt-- > 1) {
3151 do_cmd(c);
3152 } // repeat cnt
3153 dot_next();
3154 dot_skip_over_ws();
3155 break;
3156 case 21: // ctrl-U scroll up half screen
3157 dot_scroll((rows - 2) / 2, -1);
3158 break;
3159 case 25: // ctrl-Y scroll up one line
3160 dot_scroll(1, -1);
3161 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003162 case 27: // esc
Eric Andersen3f980402001-04-04 17:31:15 +00003163 if (cmd_mode == 0)
3164 indicate_error(c);
3165 cmd_mode = 0; // stop insrting
3166 end_cmd_q();
Paul Fox8552aec2005-09-16 12:20:05 +00003167 last_status_cksum = 0; // force status update
Eric Andersen3f980402001-04-04 17:31:15 +00003168 break;
3169 case ' ': // move right
3170 case 'l': // move right
3171 case VI_K_RIGHT: // Cursor Key Right
3172 if (cmdcnt-- > 1) {
3173 do_cmd(c);
3174 } // repeat cnt
3175 dot_right();
3176 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003177#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003178 case '"': // "- name a register to use for Delete/Yank
3179 c1 = get_one_char();
3180 c1 = tolower(c1);
3181 if (islower(c1)) {
3182 YDreg = c1 - 'a';
3183 } else {
3184 indicate_error(c);
3185 }
3186 break;
3187 case '\'': // '- goto a specific mark
3188 c1 = get_one_char();
3189 c1 = tolower(c1);
3190 if (islower(c1)) {
3191 c1 = c1 - 'a';
3192 // get the b-o-l
3193 q = mark[(int) c1];
3194 if (text <= q && q < end) {
3195 dot = q;
3196 dot_begin(); // go to B-o-l
3197 dot_skip_over_ws();
3198 }
3199 } else if (c1 == '\'') { // goto previous context
3200 dot = swap_context(dot); // swap current and previous context
3201 dot_begin(); // go to B-o-l
3202 dot_skip_over_ws();
3203 } else {
3204 indicate_error(c);
3205 }
3206 break;
3207 case 'm': // m- Mark a line
3208 // this is really stupid. If there are any inserts or deletes
3209 // between text[0] and dot then this mark will not point to the
3210 // correct location! It could be off by many lines!
3211 // Well..., at least its quick and dirty.
3212 c1 = get_one_char();
3213 c1 = tolower(c1);
3214 if (islower(c1)) {
3215 c1 = c1 - 'a';
3216 // remember the line
3217 mark[(int) c1] = dot;
3218 } else {
3219 indicate_error(c);
3220 }
3221 break;
3222 case 'P': // P- Put register before
3223 case 'p': // p- put register after
3224 p = reg[YDreg];
3225 if (p == 0) {
3226 psbs("Nothing in register %c", what_reg());
3227 break;
3228 }
3229 // are we putting whole lines or strings
3230 if (strchr((char *) p, '\n') != NULL) {
3231 if (c == 'P') {
3232 dot_begin(); // putting lines- Put above
3233 }
3234 if (c == 'p') {
3235 // are we putting after very last line?
3236 if (end_line(dot) == (end - 1)) {
3237 dot = end; // force dot to end of text[]
3238 } else {
3239 dot_next(); // next line, then put before
3240 }
3241 }
3242 } else {
3243 if (c == 'p')
3244 dot_right(); // move to right, can move to NL
3245 }
3246 dot = string_insert(dot, p); // insert the string
3247 end_cmd_q(); // stop adding to q
3248 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003249 case 'U': // U- Undo; replace current line with original version
3250 if (reg[Ureg] != 0) {
3251 p = begin_line(dot);
3252 q = end_line(dot);
3253 p = text_hole_delete(p, q); // delete cur line
3254 p = string_insert(p, reg[Ureg]); // insert orig line
3255 dot = p;
3256 dot_skip_over_ws();
3257 }
3258 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003259#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003260 case '$': // $- goto end of line
3261 case VI_K_END: // Cursor Key End
3262 if (cmdcnt-- > 1) {
3263 do_cmd(c);
3264 } // repeat cnt
Glenn L McGrathee829062004-01-21 10:59:45 +00003265 dot = end_line(dot);
Eric Andersen3f980402001-04-04 17:31:15 +00003266 break;
3267 case '%': // %- find matching char of pair () [] {}
3268 for (q = dot; q < end && *q != '\n'; q++) {
3269 if (strchr("()[]{}", *q) != NULL) {
3270 // we found half of a pair
3271 p = find_pair(q, *q);
3272 if (p == NULL) {
3273 indicate_error(c);
3274 } else {
3275 dot = p;
3276 }
3277 break;
3278 }
3279 }
3280 if (*q == '\n')
3281 indicate_error(c);
3282 break;
3283 case 'f': // f- forward to a user specified char
3284 last_forward_char = get_one_char(); // get the search char
3285 //
Eric Andersenaff114c2004-04-14 17:51:38 +00003286 // dont separate these two commands. 'f' depends on ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003287 //
Paul Foxd13b90b2005-07-18 22:17:25 +00003288 //**** fall thru to ... ';'
Eric Andersen3f980402001-04-04 17:31:15 +00003289 case ';': // ;- look at rest of line for last forward char
3290 if (cmdcnt-- > 1) {
Eric Andersen822c3832001-05-07 17:37:43 +00003291 do_cmd(';');
Eric Andersen3f980402001-04-04 17:31:15 +00003292 } // repeat cnt
Eric Andersen822c3832001-05-07 17:37:43 +00003293 if (last_forward_char == 0) break;
Eric Andersen3f980402001-04-04 17:31:15 +00003294 q = dot + 1;
3295 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3296 q++;
3297 }
3298 if (*q == last_forward_char)
3299 dot = q;
3300 break;
3301 case '-': // -- goto prev line
3302 if (cmdcnt-- > 1) {
3303 do_cmd(c);
3304 } // repeat cnt
3305 dot_prev();
3306 dot_skip_over_ws();
3307 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003308#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003309 case '.': // .- repeat the last modifying command
3310 // Stuff the last_modifying_cmd back into stdin
3311 // and let it be re-executed.
3312 if (last_modifying_cmd != 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +00003313 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
Eric Andersen3f980402001-04-04 17:31:15 +00003314 }
3315 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003316#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3317#ifdef CONFIG_FEATURE_VI_SEARCH
Eric Andersen3f980402001-04-04 17:31:15 +00003318 case '?': // /- search for a pattern
3319 case '/': // /- search for a pattern
3320 buf[0] = c;
3321 buf[1] = '\0';
3322 q = get_input_line(buf); // get input line- use "status line"
3323 if (strlen((char *) q) == 1)
3324 goto dc3; // if no pat re-use old pat
3325 if (strlen((char *) q) > 1) { // new pat- save it and find
3326 // there is a new pat
Aaron Lehmanna170e1c2002-11-28 11:27:31 +00003327 free(last_search_pattern);
Manuel Novoa III cad53642003-03-19 09:13:01 +00003328 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
Eric Andersen3f980402001-04-04 17:31:15 +00003329 goto dc3; // now find the pattern
3330 }
3331 // user changed mind and erased the "/"- do nothing
3332 break;
3333 case 'N': // N- backward search for last pattern
3334 if (cmdcnt-- > 1) {
3335 do_cmd(c);
3336 } // repeat cnt
3337 dir = BACK; // assume BACKWARD search
3338 p = dot - 1;
3339 if (last_search_pattern[0] == '?') {
3340 dir = FORWARD;
3341 p = dot + 1;
3342 }
3343 goto dc4; // now search for pattern
3344 break;
3345 case 'n': // n- repeat search for last pattern
3346 // search rest of text[] starting at next char
3347 // if search fails return orignal "p" not the "p+1" address
3348 if (cmdcnt-- > 1) {
3349 do_cmd(c);
3350 } // repeat cnt
3351 dc3:
3352 if (last_search_pattern == 0) {
3353 msg = (Byte *) "No previous regular expression";
3354 goto dc2;
3355 }
3356 if (last_search_pattern[0] == '/') {
3357 dir = FORWARD; // assume FORWARD search
3358 p = dot + 1;
3359 }
3360 if (last_search_pattern[0] == '?') {
3361 dir = BACK;
3362 p = dot - 1;
3363 }
3364 dc4:
3365 q = char_search(p, last_search_pattern + 1, dir, FULL);
3366 if (q != NULL) {
3367 dot = q; // good search, update "dot"
3368 msg = (Byte *) "";
3369 goto dc2;
3370 }
3371 // no pattern found between "dot" and "end"- continue at top
3372 p = text;
3373 if (dir == BACK) {
3374 p = end - 1;
3375 }
3376 q = char_search(p, last_search_pattern + 1, dir, FULL);
3377 if (q != NULL) { // found something
3378 dot = q; // found new pattern- goto it
3379 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3380 if (dir == BACK) {
3381 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3382 }
3383 } else {
3384 msg = (Byte *) "Pattern not found";
3385 }
3386 dc2:
Paul Fox8552aec2005-09-16 12:20:05 +00003387 if (*msg) psbs("%s", msg);
Eric Andersen3f980402001-04-04 17:31:15 +00003388 break;
3389 case '{': // {- move backward paragraph
3390 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3391 if (q != NULL) { // found blank line
3392 dot = next_line(q); // move to next blank line
3393 }
3394 break;
3395 case '}': // }- move forward paragraph
3396 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3397 if (q != NULL) { // found blank line
3398 dot = next_line(q); // move to next blank line
3399 }
3400 break;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003401#endif /* CONFIG_FEATURE_VI_SEARCH */
Eric Andersen3f980402001-04-04 17:31:15 +00003402 case '0': // 0- goto begining of line
Eric Andersenc7bda1c2004-03-15 08:29:22 +00003403 case '1': // 1-
3404 case '2': // 2-
3405 case '3': // 3-
3406 case '4': // 4-
3407 case '5': // 5-
3408 case '6': // 6-
3409 case '7': // 7-
3410 case '8': // 8-
3411 case '9': // 9-
Eric Andersen3f980402001-04-04 17:31:15 +00003412 if (c == '0' && cmdcnt < 1) {
3413 dot_begin(); // this was a standalone zero
3414 } else {
3415 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3416 }
3417 break;
3418 case ':': // :- the colon mode commands
Eric Andersen3f980402001-04-04 17:31:15 +00003419 p = get_input_line((Byte *) ":"); // get input line- use "status line"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003420#ifdef CONFIG_FEATURE_VI_COLON
Eric Andersen3f980402001-04-04 17:31:15 +00003421 colon(p); // execute the command
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003422#else /* CONFIG_FEATURE_VI_COLON */
Eric Andersen822c3832001-05-07 17:37:43 +00003423 if (*p == ':')
3424 p++; // move past the ':'
3425 cnt = strlen((char *) p);
3426 if (cnt <= 0)
3427 break;
3428 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3429 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
Matt Kraai1f0c4362001-12-20 23:13:26 +00003430 if (file_modified && p[1] != '!') {
Eric Andersen3f980402001-04-04 17:31:15 +00003431 psbs("No write since last change (:quit! overrides)");
3432 } else {
3433 editing = 0;
3434 }
Eric Andersen822c3832001-05-07 17:37:43 +00003435 } else if (strncasecmp((char *) p, "write", cnt) == 0 ||
Robert Griebla71389b2002-07-31 21:22:21 +00003436 strncasecmp((char *) p, "wq", cnt) == 0 ||
3437 strncasecmp((char *) p, "x", cnt) == 0) {
Eric Andersen3f980402001-04-04 17:31:15 +00003438 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003439 if (cnt < 0) {
3440 if (cnt == -1)
3441 psbs("Write error: %s", strerror(errno));
3442 } else {
3443 file_modified = 0;
3444 last_file_modified = -1;
3445 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
3446 if (p[0] == 'x' || p[1] == 'q') {
3447 editing = 0;
3448 }
Eric Andersen3f980402001-04-04 17:31:15 +00003449 }
Eric Andersen822c3832001-05-07 17:37:43 +00003450 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
Paul Fox8552aec2005-09-16 12:20:05 +00003451 last_status_cksum = 0; // force status update
Eric Andersen822c3832001-05-07 17:37:43 +00003452 } else if (sscanf((char *) p, "%d", &j) > 0) {
3453 dot = find_line(j); // go to line # j
3454 dot_skip_over_ws();
Eric Andersen3f980402001-04-04 17:31:15 +00003455 } else { // unrecognised cmd
Eric Andersen822c3832001-05-07 17:37:43 +00003456 ni((Byte *) p);
Eric Andersen3f980402001-04-04 17:31:15 +00003457 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003458#endif /* CONFIG_FEATURE_VI_COLON */
Eric Andersen3f980402001-04-04 17:31:15 +00003459 break;
3460 case '<': // <- Left shift something
3461 case '>': // >- Right shift something
3462 cnt = count_lines(text, dot); // remember what line we are on
3463 c1 = get_one_char(); // get the type of thing to delete
3464 find_range(&p, &q, c1);
3465 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3466 p = begin_line(p);
3467 q = end_line(q);
3468 i = count_lines(p, q); // # of lines we are shifting
3469 for ( ; i > 0; i--, p = next_line(p)) {
3470 if (c == '<') {
3471 // shift left- remove tab or 8 spaces
3472 if (*p == '\t') {
3473 // shrink buffer 1 char
3474 (void) text_hole_delete(p, p);
3475 } else if (*p == ' ') {
3476 // we should be calculating columns, not just SPACE
3477 for (j = 0; *p == ' ' && j < tabstop; j++) {
3478 (void) text_hole_delete(p, p);
3479 }
3480 }
3481 } else if (c == '>') {
3482 // shift right -- add tab or 8 spaces
3483 (void) char_insert(p, '\t');
3484 }
3485 }
3486 dot = find_line(cnt); // what line were we on
3487 dot_skip_over_ws();
3488 end_cmd_q(); // stop adding to q
3489 break;
3490 case 'A': // A- append at e-o-l
3491 dot_end(); // go to e-o-l
3492 //**** fall thru to ... 'a'
3493 case 'a': // a- append after current char
3494 if (*dot != '\n')
3495 dot++;
3496 goto dc_i;
3497 break;
3498 case 'B': // B- back a blank-delimited Word
3499 case 'E': // E- end of a blank-delimited word
3500 case 'W': // W- forward a blank-delimited word
3501 if (cmdcnt-- > 1) {
3502 do_cmd(c);
3503 } // repeat cnt
3504 dir = FORWARD;
3505 if (c == 'B')
3506 dir = BACK;
3507 if (c == 'W' || isspace(dot[dir])) {
3508 dot = skip_thing(dot, 1, dir, S_TO_WS);
3509 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3510 }
3511 if (c != 'W')
3512 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3513 break;
3514 case 'C': // C- Change to e-o-l
3515 case 'D': // D- delete to e-o-l
3516 save_dot = dot;
3517 dot = dollar_line(dot); // move to before NL
3518 // copy text into a register and delete
3519 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3520 if (c == 'C')
3521 goto dc_i; // start inserting
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003522#ifdef CONFIG_FEATURE_VI_DOT_CMD
Eric Andersen3f980402001-04-04 17:31:15 +00003523 if (c == 'D')
3524 end_cmd_q(); // stop adding to q
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003525#endif /* CONFIG_FEATURE_VI_DOT_CMD */
Eric Andersen3f980402001-04-04 17:31:15 +00003526 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003527 case 'G': // G- goto to a line number (default= E-O-F)
3528 dot = end - 1; // assume E-O-F
Eric Andersen1c0d3112001-04-16 15:46:44 +00003529 if (cmdcnt > 0) {
Eric Andersen822c3832001-05-07 17:37:43 +00003530 dot = find_line(cmdcnt); // what line is #cmdcnt
Eric Andersen1c0d3112001-04-16 15:46:44 +00003531 }
3532 dot_skip_over_ws();
3533 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003534 case 'H': // H- goto top line on screen
3535 dot = screenbegin;
3536 if (cmdcnt > (rows - 1)) {
3537 cmdcnt = (rows - 1);
3538 }
3539 if (cmdcnt-- > 1) {
3540 do_cmd('+');
3541 } // repeat cnt
3542 dot_skip_over_ws();
3543 break;
3544 case 'I': // I- insert before first non-blank
3545 dot_begin(); // 0
3546 dot_skip_over_ws();
3547 //**** fall thru to ... 'i'
3548 case 'i': // i- insert before current char
3549 case VI_K_INSERT: // Cursor Key Insert
3550 dc_i:
3551 cmd_mode = 1; // start insrting
Eric Andersen3f980402001-04-04 17:31:15 +00003552 break;
3553 case 'J': // J- join current and next lines together
3554 if (cmdcnt-- > 2) {
3555 do_cmd(c);
3556 } // repeat cnt
3557 dot_end(); // move to NL
3558 if (dot < end - 1) { // make sure not last char in text[]
3559 *dot++ = ' '; // replace NL with space
Paul Fox8552aec2005-09-16 12:20:05 +00003560 file_modified++;
Eric Andersen3f980402001-04-04 17:31:15 +00003561 while (isblnk(*dot)) { // delete leading WS
3562 dot_delete();
3563 }
3564 }
3565 end_cmd_q(); // stop adding to q
3566 break;
3567 case 'L': // L- goto bottom line on screen
3568 dot = end_screen();
3569 if (cmdcnt > (rows - 1)) {
3570 cmdcnt = (rows - 1);
3571 }
3572 if (cmdcnt-- > 1) {
3573 do_cmd('-');
3574 } // repeat cnt
3575 dot_begin();
3576 dot_skip_over_ws();
3577 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003578 case 'M': // M- goto middle line on screen
Eric Andersen1c0d3112001-04-16 15:46:44 +00003579 dot = screenbegin;
3580 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3581 dot = next_line(dot);
3582 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003583 case 'O': // O- open a empty line above
Eric Andersen822c3832001-05-07 17:37:43 +00003584 // 0i\n ESC -i
Eric Andersen3f980402001-04-04 17:31:15 +00003585 p = begin_line(dot);
3586 if (p[-1] == '\n') {
3587 dot_prev();
3588 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3589 dot_end();
3590 dot = char_insert(dot, '\n');
3591 } else {
3592 dot_begin(); // 0
Eric Andersen822c3832001-05-07 17:37:43 +00003593 dot = char_insert(dot, '\n'); // i\n ESC
Eric Andersen3f980402001-04-04 17:31:15 +00003594 dot_prev(); // -
3595 }
3596 goto dc_i;
3597 break;
3598 case 'R': // R- continuous Replace char
Eric Andersen1c0d3112001-04-16 15:46:44 +00003599 dc5:
Eric Andersen3f980402001-04-04 17:31:15 +00003600 cmd_mode = 2;
Eric Andersen3f980402001-04-04 17:31:15 +00003601 break;
3602 case 'X': // X- delete char before dot
3603 case 'x': // x- delete the current char
3604 case 's': // s- substitute the current char
3605 if (cmdcnt-- > 1) {
3606 do_cmd(c);
3607 } // repeat cnt
3608 dir = 0;
3609 if (c == 'X')
3610 dir = -1;
3611 if (dot[dir] != '\n') {
3612 if (c == 'X')
3613 dot--; // delete prev char
3614 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3615 }
3616 if (c == 's')
3617 goto dc_i; // start insrting
3618 end_cmd_q(); // stop adding to q
3619 break;
3620 case 'Z': // Z- if modified, {write}; exit
3621 // ZZ means to save file (if necessary), then exit
3622 c1 = get_one_char();
3623 if (c1 != 'Z') {
3624 indicate_error(c);
3625 break;
3626 }
Matt Kraai1f0c4362001-12-20 23:13:26 +00003627 if (file_modified
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003628#ifdef CONFIG_FEATURE_VI_READONLY
Matt Kraai1f0c4362001-12-20 23:13:26 +00003629 && ! vi_readonly
3630 && ! readonly
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003631#endif /* CONFIG_FEATURE_VI_READONLY */
Eric Andersen3f980402001-04-04 17:31:15 +00003632 ) {
3633 cnt = file_write(cfn, text, end - 1);
Paul Fox61e45db2005-10-09 14:43:22 +00003634 if (cnt < 0) {
3635 if (cnt == -1)
3636 psbs("Write error: %s", strerror(errno));
3637 } else if (cnt == (end - 1 - text + 1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003638 editing = 0;
3639 }
3640 } else {
3641 editing = 0;
3642 }
3643 break;
3644 case '^': // ^- move to first non-blank on line
3645 dot_begin();
3646 dot_skip_over_ws();
3647 break;
3648 case 'b': // b- back a word
3649 case 'e': // e- end of word
3650 if (cmdcnt-- > 1) {
3651 do_cmd(c);
3652 } // repeat cnt
3653 dir = FORWARD;
3654 if (c == 'b')
3655 dir = BACK;
3656 if ((dot + dir) < text || (dot + dir) > end - 1)
3657 break;
3658 dot += dir;
3659 if (isspace(*dot)) {
3660 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3661 }
3662 if (isalnum(*dot) || *dot == '_') {
3663 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3664 } else if (ispunct(*dot)) {
3665 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3666 }
3667 break;
3668 case 'c': // c- change something
3669 case 'd': // d- delete something
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003670#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003671 case 'y': // y- yank something
3672 case 'Y': // Y- Yank a line
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003673#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003674 yf = YANKDEL; // assume either "c" or "d"
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003675#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003676 if (c == 'y' || c == 'Y')
3677 yf = YANKONLY;
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003678#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003679 c1 = 'y';
3680 if (c != 'Y')
3681 c1 = get_one_char(); // get the type of thing to delete
3682 find_range(&p, &q, c1);
3683 if (c1 == 27) { // ESC- user changed mind and wants out
3684 c = c1 = 27; // Escape- do nothing
3685 } else if (strchr("wW", c1)) {
3686 if (c == 'c') {
3687 // don't include trailing WS as part of word
3688 while (isblnk(*q)) {
3689 if (q <= text || q[-1] == '\n')
3690 break;
3691 q--;
3692 }
3693 }
3694 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen822c3832001-05-07 17:37:43 +00003695 } else if (strchr("^0bBeEft$", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003696 // single line copy text into a register and delete
3697 dot = yank_delete(p, q, 0, yf); // delete word
Eric Andersen1c0d3112001-04-16 15:46:44 +00003698 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
Eric Andersen3f980402001-04-04 17:31:15 +00003699 // multiple line copy text into a register and delete
3700 dot = yank_delete(p, q, 1, yf); // delete lines
Eric Andersen1c0d3112001-04-16 15:46:44 +00003701 if (c == 'c') {
3702 dot = char_insert(dot, '\n');
3703 // on the last line of file don't move to prev line
3704 if (dot != (end-1)) {
3705 dot_prev();
3706 }
3707 } else if (c == 'd') {
Eric Andersen3f980402001-04-04 17:31:15 +00003708 dot_begin();
3709 dot_skip_over_ws();
3710 }
3711 } else {
3712 // could not recognize object
3713 c = c1 = 27; // error-
3714 indicate_error(c);
3715 }
3716 if (c1 != 27) {
3717 // if CHANGING, not deleting, start inserting after the delete
3718 if (c == 'c') {
3719 strcpy((char *) buf, "Change");
3720 goto dc_i; // start inserting
3721 }
3722 if (c == 'd') {
3723 strcpy((char *) buf, "Delete");
3724 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003725#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003726 if (c == 'y' || c == 'Y') {
3727 strcpy((char *) buf, "Yank");
3728 }
3729 p = reg[YDreg];
3730 q = p + strlen((char *) p);
3731 for (cnt = 0; p <= q; p++) {
3732 if (*p == '\n')
3733 cnt++;
3734 }
3735 psb("%s %d lines (%d chars) using [%c]",
3736 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003737#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003738 end_cmd_q(); // stop adding to q
3739 }
3740 break;
3741 case 'k': // k- goto prev line, same col
3742 case VI_K_UP: // cursor key Up
3743 if (cmdcnt-- > 1) {
3744 do_cmd(c);
3745 } // repeat cnt
3746 dot_prev();
3747 dot = move_to_col(dot, ccol + offset); // try stay in same col
3748 break;
3749 case 'r': // r- replace the current char with user input
3750 c1 = get_one_char(); // get the replacement char
3751 if (*dot != '\n') {
3752 *dot = c1;
Paul Fox8552aec2005-09-16 12:20:05 +00003753 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003754 }
3755 end_cmd_q(); // stop adding to q
3756 break;
Eric Andersen822c3832001-05-07 17:37:43 +00003757 case 't': // t- move to char prior to next x
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00003758 last_forward_char = get_one_char();
3759 do_cmd(';');
3760 if (*dot == last_forward_char)
3761 dot_left();
3762 last_forward_char= 0;
Eric Andersen822c3832001-05-07 17:37:43 +00003763 break;
Eric Andersen3f980402001-04-04 17:31:15 +00003764 case 'w': // w- forward a word
3765 if (cmdcnt-- > 1) {
3766 do_cmd(c);
3767 } // repeat cnt
3768 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3769 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3770 } else if (ispunct(*dot)) { // we are on PUNCT
3771 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3772 }
3773 if (dot < end - 1)
3774 dot++; // move over word
3775 if (isspace(*dot)) {
3776 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3777 }
3778 break;
3779 case 'z': // z-
3780 c1 = get_one_char(); // get the replacement char
3781 cnt = 0;
3782 if (c1 == '.')
3783 cnt = (rows - 2) / 2; // put dot at center
3784 if (c1 == '-')
3785 cnt = rows - 2; // put dot at bottom
3786 screenbegin = begin_line(dot); // start dot at top
3787 dot_scroll(cnt, -1);
3788 break;
3789 case '|': // |- move to column "cmdcnt"
3790 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3791 break;
3792 case '~': // ~- flip the case of letters a-z -> A-Z
3793 if (cmdcnt-- > 1) {
3794 do_cmd(c);
3795 } // repeat cnt
3796 if (islower(*dot)) {
3797 *dot = toupper(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003798 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003799 } else if (isupper(*dot)) {
3800 *dot = tolower(*dot);
Paul Fox8552aec2005-09-16 12:20:05 +00003801 file_modified++; // has the file been modified
Eric Andersen3f980402001-04-04 17:31:15 +00003802 }
3803 dot_right();
3804 end_cmd_q(); // stop adding to q
3805 break;
3806 //----- The Cursor and Function Keys -----------------------------
3807 case VI_K_HOME: // Cursor Key Home
3808 dot_begin();
3809 break;
3810 // The Fn keys could point to do_macro which could translate them
3811 case VI_K_FUN1: // Function Key F1
3812 case VI_K_FUN2: // Function Key F2
3813 case VI_K_FUN3: // Function Key F3
3814 case VI_K_FUN4: // Function Key F4
3815 case VI_K_FUN5: // Function Key F5
3816 case VI_K_FUN6: // Function Key F6
3817 case VI_K_FUN7: // Function Key F7
3818 case VI_K_FUN8: // Function Key F8
3819 case VI_K_FUN9: // Function Key F9
3820 case VI_K_FUN10: // Function Key F10
3821 case VI_K_FUN11: // Function Key F11
3822 case VI_K_FUN12: // Function Key F12
3823 break;
3824 }
3825
3826 dc1:
3827 // if text[] just became empty, add back an empty line
3828 if (end == text) {
3829 (void) char_insert(text, '\n'); // start empty buf with dummy line
3830 dot = text;
3831 }
3832 // it is OK for dot to exactly equal to end, otherwise check dot validity
3833 if (dot != end) {
3834 dot = bound_dot(dot); // make sure "dot" is valid
3835 }
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003836#ifdef CONFIG_FEATURE_VI_YANKMARK
Eric Andersen3f980402001-04-04 17:31:15 +00003837 check_context(c); // update the current context
Eric Andersenbdfd0d72001-10-24 05:00:29 +00003838#endif /* CONFIG_FEATURE_VI_YANKMARK */
Eric Andersen3f980402001-04-04 17:31:15 +00003839
3840 if (!isdigit(c))
3841 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3842 cnt = dot - begin_line(dot);
3843 // Try to stay off of the Newline
3844 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3845 dot--;
3846}
Glenn L McGrath09adaca2002-12-02 21:18:10 +00003847
3848#ifdef CONFIG_FEATURE_VI_CRASHME
3849static int totalcmds = 0;
3850static int Mp = 85; // Movement command Probability
3851static int Np = 90; // Non-movement command Probability
3852static int Dp = 96; // Delete command Probability
3853static int Ip = 97; // Insert command Probability
3854static int Yp = 98; // Yank command Probability
3855static int Pp = 99; // Put command Probability
3856static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3857char chars[20] = "\t012345 abcdABCD-=.$";
3858char *words[20] = { "this", "is", "a", "test",
3859 "broadcast", "the", "emergency", "of",
3860 "system", "quick", "brown", "fox",
3861 "jumped", "over", "lazy", "dogs",
3862 "back", "January", "Febuary", "March"
3863};
3864char *lines[20] = {
3865 "You should have received a copy of the GNU General Public License\n",
3866 "char c, cm, *cmd, *cmd1;\n",
3867 "generate a command by percentages\n",
3868 "Numbers may be typed as a prefix to some commands.\n",
3869 "Quit, discarding changes!\n",
3870 "Forced write, if permission originally not valid.\n",
3871 "In general, any ex or ed command (such as substitute or delete).\n",
3872 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3873 "Please get w/ me and I will go over it with you.\n",
3874 "The following is a list of scheduled, committed changes.\n",
3875 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3876 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3877 "Any question about transactions please contact Sterling Huxley.\n",
3878 "I will try to get back to you by Friday, December 31.\n",
3879 "This Change will be implemented on Friday.\n",
3880 "Let me know if you have problems accessing this;\n",
3881 "Sterling Huxley recently added you to the access list.\n",
3882 "Would you like to go to lunch?\n",
3883 "The last command will be automatically run.\n",
3884 "This is too much english for a computer geek.\n",
3885};
3886char *multilines[20] = {
3887 "You should have received a copy of the GNU General Public License\n",
3888 "char c, cm, *cmd, *cmd1;\n",
3889 "generate a command by percentages\n",
3890 "Numbers may be typed as a prefix to some commands.\n",
3891 "Quit, discarding changes!\n",
3892 "Forced write, if permission originally not valid.\n",
3893 "In general, any ex or ed command (such as substitute or delete).\n",
3894 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3895 "Please get w/ me and I will go over it with you.\n",
3896 "The following is a list of scheduled, committed changes.\n",
3897 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3898 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3899 "Any question about transactions please contact Sterling Huxley.\n",
3900 "I will try to get back to you by Friday, December 31.\n",
3901 "This Change will be implemented on Friday.\n",
3902 "Let me know if you have problems accessing this;\n",
3903 "Sterling Huxley recently added you to the access list.\n",
3904 "Would you like to go to lunch?\n",
3905 "The last command will be automatically run.\n",
3906 "This is too much english for a computer geek.\n",
3907};
3908
3909// create a random command to execute
3910static void crash_dummy()
3911{
3912 static int sleeptime; // how long to pause between commands
3913 char c, cm, *cmd, *cmd1;
3914 int i, cnt, thing, rbi, startrbi, percent;
3915
3916 // "dot" movement commands
3917 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3918
3919 // is there already a command running?
3920 if (readed_for_parse > 0)
3921 goto cd1;
3922 cd0:
3923 startrbi = rbi = 0;
3924 sleeptime = 0; // how long to pause between commands
3925 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3926 // generate a command by percentages
3927 percent = (int) lrand48() % 100; // get a number from 0-99
3928 if (percent < Mp) { // Movement commands
3929 // available commands
3930 cmd = cmd1;
3931 M++;
3932 } else if (percent < Np) { // non-movement commands
3933 cmd = "mz<>\'\""; // available commands
3934 N++;
3935 } else if (percent < Dp) { // Delete commands
3936 cmd = "dx"; // available commands
3937 D++;
3938 } else if (percent < Ip) { // Inset commands
3939 cmd = "iIaAsrJ"; // available commands
3940 I++;
3941 } else if (percent < Yp) { // Yank commands
3942 cmd = "yY"; // available commands
3943 Y++;
3944 } else if (percent < Pp) { // Put commands
3945 cmd = "pP"; // available commands
3946 P++;
3947 } else {
3948 // We do not know how to handle this command, try again
3949 U++;
3950 goto cd0;
3951 }
3952 // randomly pick one of the available cmds from "cmd[]"
3953 i = (int) lrand48() % strlen(cmd);
3954 cm = cmd[i];
3955 if (strchr(":\024", cm))
3956 goto cd0; // dont allow colon or ctrl-T commands
3957 readbuffer[rbi++] = cm; // put cmd into input buffer
3958
3959 // now we have the command-
3960 // there are 1, 2, and multi char commands
3961 // find out which and generate the rest of command as necessary
3962 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3963 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3964 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3965 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3966 }
3967 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3968 c = cmd1[thing];
3969 readbuffer[rbi++] = c; // add movement to input buffer
3970 }
3971 if (strchr("iIaAsc", cm)) { // multi-char commands
3972 if (cm == 'c') {
3973 // change some thing
3974 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3975 c = cmd1[thing];
3976 readbuffer[rbi++] = c; // add movement to input buffer
3977 }
3978 thing = (int) lrand48() % 4; // what thing to insert
3979 cnt = (int) lrand48() % 10; // how many to insert
3980 for (i = 0; i < cnt; i++) {
3981 if (thing == 0) { // insert chars
3982 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3983 } else if (thing == 1) { // insert words
3984 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3985 strcat((char *) readbuffer, " ");
3986 sleeptime = 0; // how fast to type
3987 } else if (thing == 2) { // insert lines
3988 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3989 sleeptime = 0; // how fast to type
3990 } else { // insert multi-lines
3991 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3992 sleeptime = 0; // how fast to type
3993 }
3994 }
3995 strcat((char *) readbuffer, "\033");
3996 }
3997 readed_for_parse = strlen(readbuffer);
3998 cd1:
3999 totalcmds++;
4000 if (sleeptime > 0)
4001 (void) mysleep(sleeptime); // sleep 1/100 sec
4002}
4003
4004// test to see if there are any errors
4005static void crash_test()
4006{
4007 static time_t oldtim;
4008 time_t tim;
Glenn L McGrath7127b582002-12-03 21:48:15 +00004009 char d[2], msg[BUFSIZ];
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004010
4011 msg[0] = '\0';
4012 if (end < text) {
4013 strcat((char *) msg, "end<text ");
4014 }
4015 if (end > textend) {
4016 strcat((char *) msg, "end>textend ");
4017 }
4018 if (dot < text) {
4019 strcat((char *) msg, "dot<text ");
4020 }
4021 if (dot > end) {
4022 strcat((char *) msg, "dot>end ");
4023 }
4024 if (screenbegin < text) {
4025 strcat((char *) msg, "screenbegin<text ");
4026 }
4027 if (screenbegin > end - 1) {
4028 strcat((char *) msg, "screenbegin>end-1 ");
4029 }
4030
4031 if (strlen(msg) > 0) {
4032 alarm(0);
Glenn L McGrath7127b582002-12-03 21:48:15 +00004033 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
Glenn L McGrath09adaca2002-12-02 21:18:10 +00004034 totalcmds, last_input_char, msg, SOs, SOn);
4035 fflush(stdout);
4036 while (read(0, d, 1) > 0) {
4037 if (d[0] == '\n' || d[0] == '\r')
4038 break;
4039 }
4040 alarm(3);
4041 }
4042 tim = (time_t) time((time_t *) 0);
4043 if (tim >= (oldtim + 3)) {
4044 sprintf((char *) status_buffer,
4045 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
4046 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
4047 oldtim = tim;
4048 }
4049 return;
4050}
Tim Rikerc1ef7bd2006-01-25 00:08:53 +00004051#endif /* CONFIG_FEATURE_VI_CRASHME */