source: MondoRescue/trunk/mindi-busybox/editors/vi.c@ 904

Last change on this file since 904 was 821, checked in by Bruno Cornec, 18 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 110.6 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * tiny vi.c: A small 'vi' clone
4 * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
5 *
6 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
7 */
8
9/*
10 * Things To Do:
11 * EXINIT
12 * $HOME/.exrc and ./.exrc
13 * add magic to search /foo.*bar
14 * add :help command
15 * :map macros
16 * how about mode lines: vi: set sw=8 ts=8:
17 * if mark[] values were line numbers rather than pointers
18 * it would be easier to change the mark when add/delete lines
19 * More intelligence in refresh()
20 * ":r !cmd" and "!cmd" to filter text through an external command
21 * A true "undo" facility
22 * An "ex" line oriented mode- maybe using "cmdedit"
23 */
24
25
26#include "busybox.h"
27#include <string.h>
28#include <strings.h>
29#include <unistd.h>
30#include <sys/ioctl.h>
31#include <time.h>
32#include <fcntl.h>
33#include <signal.h>
34#include <setjmp.h>
35#include <regex.h>
36#include <ctype.h>
37#include <errno.h>
38#define vi_Version BB_VER " " BB_BT
39
40#ifdef CONFIG_LOCALE_SUPPORT
41#define Isprint(c) isprint((c))
42#else
43#define Isprint(c) ( (c) >= ' ' && (c) != 127 && (c) != ((unsigned char)'\233') )
44#endif
45
46#define MAX_SCR_COLS BUFSIZ
47
48// Misc. non-Ascii keys that report an escape sequence
49#define VI_K_UP 128 // cursor key Up
50#define VI_K_DOWN 129 // cursor key Down
51#define VI_K_RIGHT 130 // Cursor Key Right
52#define VI_K_LEFT 131 // cursor key Left
53#define VI_K_HOME 132 // Cursor Key Home
54#define VI_K_END 133 // Cursor Key End
55#define VI_K_INSERT 134 // Cursor Key Insert
56#define VI_K_PAGEUP 135 // Cursor Key Page Up
57#define VI_K_PAGEDOWN 136 // Cursor Key Page Down
58#define VI_K_FUN1 137 // Function Key F1
59#define VI_K_FUN2 138 // Function Key F2
60#define VI_K_FUN3 139 // Function Key F3
61#define VI_K_FUN4 140 // Function Key F4
62#define VI_K_FUN5 141 // Function Key F5
63#define VI_K_FUN6 142 // Function Key F6
64#define VI_K_FUN7 143 // Function Key F7
65#define VI_K_FUN8 144 // Function Key F8
66#define VI_K_FUN9 145 // Function Key F9
67#define VI_K_FUN10 146 // Function Key F10
68#define VI_K_FUN11 147 // Function Key F11
69#define VI_K_FUN12 148 // Function Key F12
70
71/* vt102 typical ESC sequence */
72/* terminal standout start/normal ESC sequence */
73static const char SOs[] = "\033[7m";
74static const char SOn[] = "\033[0m";
75/* terminal bell sequence */
76static const char bell[] = "\007";
77/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
78static const char Ceol[] = "\033[0K";
79static const char Ceos [] = "\033[0J";
80/* Cursor motion arbitrary destination ESC sequence */
81static const char CMrc[] = "\033[%d;%dH";
82/* Cursor motion up and down ESC sequence */
83static const char CMup[] = "\033[A";
84static const char CMdown[] = "\n";
85
86
87enum {
88 YANKONLY = FALSE,
89 YANKDEL = TRUE,
90 FORWARD = 1, // code depends on "1" for array index
91 BACK = -1, // code depends on "-1" for array index
92 LIMITED = 0, // how much of text[] in char_search
93 FULL = 1, // how much of text[] in char_search
94
95 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
96 S_TO_WS = 2, // used in skip_thing() for moving "dot"
97 S_OVER_WS = 3, // used in skip_thing() for moving "dot"
98 S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
99 S_END_ALNUM = 5 // used in skip_thing() for moving "dot"
100};
101
102typedef unsigned char Byte;
103
104static int vi_setops;
105#define VI_AUTOINDENT 1
106#define VI_SHOWMATCH 2
107#define VI_IGNORECASE 4
108#define VI_ERR_METHOD 8
109#define autoindent (vi_setops & VI_AUTOINDENT)
110#define showmatch (vi_setops & VI_SHOWMATCH )
111#define ignorecase (vi_setops & VI_IGNORECASE)
112/* indicate error with beep or flash */
113#define err_method (vi_setops & VI_ERR_METHOD)
114
115
116static int editing; // >0 while we are editing a file
117static int cmd_mode; // 0=command 1=insert 2=replace
118static int file_modified; // buffer contents changed
119static int last_file_modified = -1;
120static int fn_start; // index of first cmd line file name
121static int save_argc; // how many file names on cmd line
122static int cmdcnt; // repetition count
123static fd_set rfds; // use select() for small sleeps
124static struct timeval tv; // use select() for small sleeps
125static int rows, columns; // the terminal screen is this size
126static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset
127static Byte *status_buffer; // mesages to the user
128#define STATUS_BUFFER_LEN 200
129static int have_status_msg; // is default edit status needed?
130static int last_status_cksum; // hash of current status line
131static Byte *cfn; // previous, current, and next file name
132static Byte *text, *end, *textend; // pointers to the user data in memory
133static Byte *screen; // pointer to the virtual screen buffer
134static int screensize; // and its size
135static Byte *screenbegin; // index into text[], of top line on the screen
136static Byte *dot; // where all the action takes place
137static int tabstop;
138static struct termios term_orig, term_vi; // remember what the cooked mode was
139static Byte erase_char; // the users erase character
140static Byte last_input_char; // last char read from user
141static Byte last_forward_char; // last char searched for with 'f'
142
143#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
144static int last_row; // where the cursor was last moved to
145#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
146#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
147static jmp_buf restart; // catch_sig()
148#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
149#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
150static int my_pid;
151#endif
152#ifdef CONFIG_FEATURE_VI_DOT_CMD
153static int adding2q; // are we currently adding user input to q
154static Byte *last_modifying_cmd; // last modifying cmd for "."
155static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read"
156#endif /* CONFIG_FEATURE_VI_DOT_CMD */
157#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
158static Byte *modifying_cmds; // cmds that modify text[]
159#endif /* CONFIG_FEATURE_VI_DOT_CMD || CONFIG_FEATURE_VI_YANKMARK */
160#ifdef CONFIG_FEATURE_VI_READONLY
161static int vi_readonly, readonly;
162#endif /* CONFIG_FEATURE_VI_READONLY */
163#ifdef CONFIG_FEATURE_VI_YANKMARK
164static Byte *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
165static int YDreg, Ureg; // default delete register and orig line for "U"
166static Byte *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
167static Byte *context_start, *context_end;
168#endif /* CONFIG_FEATURE_VI_YANKMARK */
169#ifdef CONFIG_FEATURE_VI_SEARCH
170static Byte *last_search_pattern; // last pattern from a '/' or '?' search
171#endif /* CONFIG_FEATURE_VI_SEARCH */
172
173
174static void edit_file(Byte *); // edit one file
175static void do_cmd(Byte); // execute a command
176static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot
177static Byte *begin_line(Byte *); // return pointer to cur line B-o-l
178static Byte *end_line(Byte *); // return pointer to cur line E-o-l
179static Byte *prev_line(Byte *); // return pointer to prev line B-o-l
180static Byte *next_line(Byte *); // return pointer to next line B-o-l
181static Byte *end_screen(void); // get pointer to last char on screen
182static int count_lines(Byte *, Byte *); // count line from start to stop
183static Byte *find_line(int); // find begining of line #li
184static Byte *move_to_col(Byte *, int); // move "p" to column l
185static int isblnk(Byte); // is the char a blank or tab
186static void dot_left(void); // move dot left- dont leave line
187static void dot_right(void); // move dot right- dont leave line
188static void dot_begin(void); // move dot to B-o-l
189static void dot_end(void); // move dot to E-o-l
190static void dot_next(void); // move dot to next line B-o-l
191static void dot_prev(void); // move dot to prev line B-o-l
192static void dot_scroll(int, int); // move the screen up or down
193static void dot_skip_over_ws(void); // move dot pat WS
194static void dot_delete(void); // delete the char at 'dot'
195static Byte *bound_dot(Byte *); // make sure text[0] <= P < "end"
196static Byte *new_screen(int, int); // malloc virtual screen memory
197static Byte *new_text(int); // malloc memory for text[] buffer
198static Byte *char_insert(Byte *, Byte); // insert the char c at 'p'
199static Byte *stupid_insert(Byte *, Byte); // stupidly insert the char c at 'p'
200static Byte find_range(Byte **, Byte **, Byte); // return pointers for an object
201static int st_test(Byte *, int, int, Byte *); // helper for skip_thing()
202static Byte *skip_thing(Byte *, int, int, int); // skip some object
203static Byte *find_pair(Byte *, Byte); // find matching pair () [] {}
204static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte hole
205static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole
206static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete
207static void show_help(void); // display some help info
208static void rawmode(void); // set "raw" mode on tty
209static void cookmode(void); // return to "cooked" mode on tty
210static int mysleep(int); // sleep for 'h' 1/100 seconds
211static Byte readit(void); // read (maybe cursor) key from stdin
212static Byte get_one_char(void); // read 1 char from stdin
213static int file_size(const Byte *); // what is the byte size of "fn"
214static int file_insert(Byte *, Byte *, int);
215static int file_write(Byte *, Byte *, Byte *);
216static void place_cursor(int, int, int);
217static void screen_erase(void);
218static void clear_to_eol(void);
219static void clear_to_eos(void);
220static void standout_start(void); // send "start reverse video" sequence
221static void standout_end(void); // send "end reverse video" sequence
222static void flash(int); // flash the terminal screen
223static void show_status_line(void); // put a message on the bottom line
224static void psb(const char *, ...); // Print Status Buf
225static void psbs(const char *, ...); // Print Status Buf in standout mode
226static void ni(Byte *); // display messages
227static int format_edit_status(void); // format file status on status line
228static void redraw(int); // force a full screen refresh
229static void format_line(Byte*, Byte*, int);
230static void refresh(int); // update the terminal from screen[]
231
232static void Indicate_Error(void); // use flash or beep to indicate error
233#define indicate_error(c) Indicate_Error()
234static void Hit_Return(void);
235
236#ifdef CONFIG_FEATURE_VI_SEARCH
237static Byte *char_search(Byte *, Byte *, int, int); // search for pattern starting at p
238static int mycmp(Byte *, Byte *, int); // string cmp based in "ignorecase"
239#endif /* CONFIG_FEATURE_VI_SEARCH */
240#ifdef CONFIG_FEATURE_VI_COLON
241static Byte *get_one_address(Byte *, int *); // get colon addr, if present
242static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present
243static void colon(Byte *); // execute the "colon" mode cmds
244#endif /* CONFIG_FEATURE_VI_COLON */
245#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
246static void winch_sig(int); // catch window size changes
247static void suspend_sig(int); // catch ctrl-Z
248static void catch_sig(int); // catch ctrl-C and alarm time-outs
249#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
250#ifdef CONFIG_FEATURE_VI_DOT_CMD
251static void start_new_cmd_q(Byte); // new queue for command
252static void end_cmd_q(void); // stop saving input chars
253#else /* CONFIG_FEATURE_VI_DOT_CMD */
254#define end_cmd_q()
255#endif /* CONFIG_FEATURE_VI_DOT_CMD */
256#ifdef CONFIG_FEATURE_VI_SETOPTS
257static void showmatching(Byte *); // show the matching pair () [] {}
258#endif /* CONFIG_FEATURE_VI_SETOPTS */
259#if defined(CONFIG_FEATURE_VI_YANKMARK) || (defined(CONFIG_FEATURE_VI_COLON) && defined(CONFIG_FEATURE_VI_SEARCH)) || defined(CONFIG_FEATURE_VI_CRASHME)
260static Byte *string_insert(Byte *, Byte *); // insert the string at 'p'
261#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_SEARCH || CONFIG_FEATURE_VI_CRASHME */
262#ifdef CONFIG_FEATURE_VI_YANKMARK
263static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register
264static Byte what_reg(void); // what is letter of current YDreg
265static void check_context(Byte); // remember context for '' command
266#endif /* CONFIG_FEATURE_VI_YANKMARK */
267#ifdef CONFIG_FEATURE_VI_CRASHME
268static void crash_dummy();
269static void crash_test();
270static int crashme = 0;
271#endif /* CONFIG_FEATURE_VI_CRASHME */
272
273
274static void write1(const char *out)
275{
276 fputs(out, stdout);
277}
278
279int vi_main(int argc, char **argv)
280{
281 int c;
282 RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
283
284#ifdef CONFIG_FEATURE_VI_YANKMARK
285 int i;
286#endif /* CONFIG_FEATURE_VI_YANKMARK */
287#if defined(CONFIG_FEATURE_VI_USE_SIGNALS) || defined(CONFIG_FEATURE_VI_CRASHME)
288 my_pid = getpid();
289#endif
290#ifdef CONFIG_FEATURE_VI_CRASHME
291 (void) srand((long) my_pid);
292#endif /* CONFIG_FEATURE_VI_CRASHME */
293
294 status_buffer = (Byte *)STATUS_BUFFER;
295 last_status_cksum = 0;
296
297#ifdef CONFIG_FEATURE_VI_READONLY
298 vi_readonly = readonly = FALSE;
299 if (strncmp(argv[0], "view", 4) == 0) {
300 readonly = TRUE;
301 vi_readonly = TRUE;
302 }
303#endif /* CONFIG_FEATURE_VI_READONLY */
304 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
305#ifdef CONFIG_FEATURE_VI_YANKMARK
306 for (i = 0; i < 28; i++) {
307 reg[i] = 0;
308 } // init the yank regs
309#endif /* CONFIG_FEATURE_VI_YANKMARK */
310#if defined(CONFIG_FEATURE_VI_DOT_CMD) || defined(CONFIG_FEATURE_VI_YANKMARK)
311 modifying_cmds = (Byte *) "aAcCdDiIJoOpPrRsxX<>~"; // cmds modifying text[]
312#endif /* CONFIG_FEATURE_VI_DOT_CMD */
313
314 // 1- process $HOME/.exrc file
315 // 2- process EXINIT variable from environment
316 // 3- process command line args
317 while ((c = getopt(argc, argv, "hCR")) != -1) {
318 switch (c) {
319#ifdef CONFIG_FEATURE_VI_CRASHME
320 case 'C':
321 crashme = 1;
322 break;
323#endif /* CONFIG_FEATURE_VI_CRASHME */
324#ifdef CONFIG_FEATURE_VI_READONLY
325 case 'R': // Read-only flag
326 readonly = TRUE;
327 vi_readonly = TRUE;
328 break;
329#endif /* CONFIG_FEATURE_VI_READONLY */
330 //case 'r': // recover flag- ignore- we don't use tmp file
331 //case 'x': // encryption flag- ignore
332 //case 'c': // execute command first
333 //case 'h': // help -- just use default
334 default:
335 show_help();
336 return 1;
337 }
338 }
339
340 // The argv array can be used by the ":next" and ":rewind" commands
341 // save optind.
342 fn_start = optind; // remember first file name for :next and :rew
343 save_argc = argc;
344
345 //----- This is the main file handling loop --------------
346 if (optind >= argc) {
347 editing = 1; // 0= exit, 1= one file, 2= multiple files
348 edit_file(0);
349 } else {
350 for (; optind < argc; optind++) {
351 editing = 1; // 0=exit, 1=one file, 2+ =many files
352 free(cfn);
353 cfn = (Byte *) bb_xstrdup(argv[optind]);
354 edit_file(cfn);
355 }
356 }
357 //-----------------------------------------------------------
358
359 return (0);
360}
361
362static void edit_file(Byte * fn)
363{
364 Byte c;
365 int cnt, size, ch;
366
367#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
368 int sig;
369#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
370#ifdef CONFIG_FEATURE_VI_YANKMARK
371 static Byte *cur_line;
372#endif /* CONFIG_FEATURE_VI_YANKMARK */
373
374 rawmode();
375 rows = 24;
376 columns = 80;
377 ch= -1;
378 if (ENABLE_FEATURE_VI_WIN_RESIZE)
379 get_terminal_width_height(0, &columns, &rows);
380 new_screen(rows, columns); // get memory for virtual screen
381
382 cnt = file_size(fn); // file size
383 size = 2 * cnt; // 200% of file size
384 new_text(size); // get a text[] buffer
385 screenbegin = dot = end = text;
386 if (fn != 0) {
387 ch= file_insert(fn, text, cnt);
388 }
389 if (ch < 1) {
390 (void) char_insert(text, '\n'); // start empty buf with dummy line
391 }
392 file_modified = 0;
393 last_file_modified = -1;
394#ifdef CONFIG_FEATURE_VI_YANKMARK
395 YDreg = 26; // default Yank/Delete reg
396 Ureg = 27; // hold orig line for "U" cmd
397 for (cnt = 0; cnt < 28; cnt++) {
398 mark[cnt] = 0;
399 } // init the marks
400 mark[26] = mark[27] = text; // init "previous context"
401#endif /* CONFIG_FEATURE_VI_YANKMARK */
402
403 last_forward_char = last_input_char = '\0';
404 crow = 0;
405 ccol = 0;
406
407#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
408 catch_sig(0);
409 signal(SIGWINCH, winch_sig);
410 signal(SIGTSTP, suspend_sig);
411 sig = setjmp(restart);
412 if (sig != 0) {
413 screenbegin = dot = text;
414 }
415#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
416
417 editing = 1;
418 cmd_mode = 0; // 0=command 1=insert 2='R'eplace
419 cmdcnt = 0;
420 tabstop = 8;
421 offset = 0; // no horizontal offset
422 c = '\0';
423#ifdef CONFIG_FEATURE_VI_DOT_CMD
424 free(last_modifying_cmd);
425 free(ioq_start);
426 ioq = ioq_start = last_modifying_cmd = 0;
427 adding2q = 0;
428#endif /* CONFIG_FEATURE_VI_DOT_CMD */
429 redraw(FALSE); // dont force every col re-draw
430 show_status_line();
431
432 //------This is the main Vi cmd handling loop -----------------------
433 while (editing > 0) {
434#ifdef CONFIG_FEATURE_VI_CRASHME
435 if (crashme > 0) {
436 if ((end - text) > 1) {
437 crash_dummy(); // generate a random command
438 } else {
439 crashme = 0;
440 dot =
441 string_insert(text, (Byte *) "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
442 refresh(FALSE);
443 }
444 }
445#endif /* CONFIG_FEATURE_VI_CRASHME */
446 last_input_char = c = get_one_char(); // get a cmd from user
447#ifdef CONFIG_FEATURE_VI_YANKMARK
448 // save a copy of the current line- for the 'U" command
449 if (begin_line(dot) != cur_line) {
450 cur_line = begin_line(dot);
451 text_yank(begin_line(dot), end_line(dot), Ureg);
452 }
453#endif /* CONFIG_FEATURE_VI_YANKMARK */
454#ifdef CONFIG_FEATURE_VI_DOT_CMD
455 // These are commands that change text[].
456 // Remember the input for the "." command
457 if (!adding2q && ioq_start == 0
458 && strchr((char *) modifying_cmds, c) != NULL) {
459 start_new_cmd_q(c);
460 }
461#endif /* CONFIG_FEATURE_VI_DOT_CMD */
462 do_cmd(c); // execute the user command
463 //
464 // poll to see if there is input already waiting. if we are
465 // not able to display output fast enough to keep up, skip
466 // the display update until we catch up with input.
467 if (mysleep(0) == 0) {
468 // no input pending- so update output
469 refresh(FALSE);
470 show_status_line();
471 }
472#ifdef CONFIG_FEATURE_VI_CRASHME
473 if (crashme > 0)
474 crash_test(); // test editor variables
475#endif /* CONFIG_FEATURE_VI_CRASHME */
476 }
477 //-------------------------------------------------------------------
478
479 place_cursor(rows, 0, FALSE); // go to bottom of screen
480 clear_to_eol(); // Erase to end of line
481 cookmode();
482}
483
484//----- The Colon commands -------------------------------------
485#ifdef CONFIG_FEATURE_VI_COLON
486static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present
487{
488 int st;
489 Byte *q;
490
491#ifdef CONFIG_FEATURE_VI_YANKMARK
492 Byte c;
493#endif /* CONFIG_FEATURE_VI_YANKMARK */
494#ifdef CONFIG_FEATURE_VI_SEARCH
495 Byte *pat, buf[BUFSIZ];
496#endif /* CONFIG_FEATURE_VI_SEARCH */
497
498 *addr = -1; // assume no addr
499 if (*p == '.') { // the current line
500 p++;
501 q = begin_line(dot);
502 *addr = count_lines(text, q);
503#ifdef CONFIG_FEATURE_VI_YANKMARK
504 } else if (*p == '\'') { // is this a mark addr
505 p++;
506 c = tolower(*p);
507 p++;
508 if (c >= 'a' && c <= 'z') {
509 // we have a mark
510 c = c - 'a';
511 q = mark[(int) c];
512 if (q != NULL) { // is mark valid
513 *addr = count_lines(text, q); // count lines
514 }
515 }
516#endif /* CONFIG_FEATURE_VI_YANKMARK */
517#ifdef CONFIG_FEATURE_VI_SEARCH
518 } else if (*p == '/') { // a search pattern
519 q = buf;
520 for (p++; *p; p++) {
521 if (*p == '/')
522 break;
523 *q++ = *p;
524 *q = '\0';
525 }
526 pat = (Byte *) bb_xstrdup((char *) buf); // save copy of pattern
527 if (*p == '/')
528 p++;
529 q = char_search(dot, pat, FORWARD, FULL);
530 if (q != NULL) {
531 *addr = count_lines(text, q);
532 }
533 free(pat);
534#endif /* CONFIG_FEATURE_VI_SEARCH */
535 } else if (*p == '$') { // the last line in file
536 p++;
537 q = begin_line(end - 1);
538 *addr = count_lines(text, q);
539 } else if (isdigit(*p)) { // specific line number
540 sscanf((char *) p, "%d%n", addr, &st);
541 p += st;
542 } else { // I don't reconise this
543 // unrecognised address- assume -1
544 *addr = -1;
545 }
546 return (p);
547}
548
549static Byte *get_address(Byte *p, int *b, int *e) // get two colon addrs, if present
550{
551 //----- get the address' i.e., 1,3 'a,'b -----
552 // get FIRST addr, if present
553 while (isblnk(*p))
554 p++; // skip over leading spaces
555 if (*p == '%') { // alias for 1,$
556 p++;
557 *b = 1;
558 *e = count_lines(text, end-1);
559 goto ga0;
560 }
561 p = get_one_address(p, b);
562 while (isblnk(*p))
563 p++;
564 if (*p == ',') { // is there a address separator
565 p++;
566 while (isblnk(*p))
567 p++;
568 // get SECOND addr, if present
569 p = get_one_address(p, e);
570 }
571ga0:
572 while (isblnk(*p))
573 p++; // skip over trailing spaces
574 return (p);
575}
576
577#ifdef CONFIG_FEATURE_VI_SETOPTS
578static void setops(const Byte *args, const char *opname, int flg_no,
579 const char *short_opname, int opt)
580{
581 const char *a = (char *) args + flg_no;
582 int l = strlen(opname) - 1; /* opname have + ' ' */
583
584 if (strncasecmp(a, opname, l) == 0 ||
585 strncasecmp(a, short_opname, 2) == 0) {
586 if(flg_no)
587 vi_setops &= ~opt;
588 else
589 vi_setops |= opt;
590 }
591}
592#endif
593
594static void colon(Byte * buf)
595{
596 Byte c, *orig_buf, *buf1, *q, *r;
597 Byte *fn, cmd[BUFSIZ], args[BUFSIZ];
598 int i, l, li, ch, st, b, e;
599 int useforce = FALSE, forced = FALSE;
600 struct stat st_buf;
601
602 // :3154 // if (-e line 3154) goto it else stay put
603 // :4,33w! foo // write a portion of buffer to file "foo"
604 // :w // write all of buffer to current file
605 // :q // quit
606 // :q! // quit- dont care about modified file
607 // :'a,'z!sort -u // filter block through sort
608 // :'f // goto mark "f"
609 // :'fl // list literal the mark "f" line
610 // :.r bar // read file "bar" into buffer before dot
611 // :/123/,/abc/d // delete lines from "123" line to "abc" line
612 // :/xyz/ // goto the "xyz" line
613 // :s/find/replace/ // substitute pattern "find" with "replace"
614 // :!<cmd> // run <cmd> then return
615 //
616
617 if (strlen((char *) buf) <= 0)
618 goto vc1;
619 if (*buf == ':')
620 buf++; // move past the ':'
621
622 li = st = ch = i = 0;
623 b = e = -1;
624 q = text; // assume 1,$ for the range
625 r = end - 1;
626 li = count_lines(text, end - 1);
627 fn = cfn; // default to current file
628 memset(cmd, '\0', BUFSIZ); // clear cmd[]
629 memset(args, '\0', BUFSIZ); // clear args[]
630
631 // look for optional address(es) :. :1 :1,9 :'q,'a :%
632 buf = get_address(buf, &b, &e);
633
634 // remember orig command line
635 orig_buf = buf;
636
637 // get the COMMAND into cmd[]
638 buf1 = cmd;
639 while (*buf != '\0') {
640 if (isspace(*buf))
641 break;
642 *buf1++ = *buf++;
643 }
644 // get any ARGuments
645 while (isblnk(*buf))
646 buf++;
647 strcpy((char *) args, (char *) buf);
648 buf1 = (Byte*)last_char_is((char *)cmd, '!');
649 if (buf1) {
650 useforce = TRUE;
651 *buf1 = '\0'; // get rid of !
652 }
653 if (b >= 0) {
654 // if there is only one addr, then the addr
655 // is the line number of the single line the
656 // user wants. So, reset the end
657 // pointer to point at end of the "b" line
658 q = find_line(b); // what line is #b
659 r = end_line(q);
660 li = 1;
661 }
662 if (e >= 0) {
663 // we were given two addrs. change the
664 // end pointer to the addr given by user.
665 r = find_line(e); // what line is #e
666 r = end_line(r);
667 li = e - b + 1;
668 }
669 // ------------ now look for the command ------------
670 i = strlen((char *) cmd);
671 if (i == 0) { // :123CR goto line #123
672 if (b >= 0) {
673 dot = find_line(b); // what line is #b
674 dot_skip_over_ws();
675 }
676 } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd
677 // :!ls run the <cmd>
678 (void) alarm(0); // wait for input- no alarms
679 place_cursor(rows - 1, 0, FALSE); // go to Status line
680 clear_to_eol(); // clear the line
681 cookmode();
682 system((char*)(orig_buf+1)); // run the cmd
683 rawmode();
684 Hit_Return(); // let user see results
685 (void) alarm(3); // done waiting for input
686 } else if (strncmp((char *) cmd, "=", i) == 0) { // where is the address
687 if (b < 0) { // no addr given- use defaults
688 b = e = count_lines(text, dot);
689 }
690 psb("%d", b);
691 } else if (strncasecmp((char *) cmd, "delete", i) == 0) { // delete lines
692 if (b < 0) { // no addr given- use defaults
693 q = begin_line(dot); // assume .,. for the range
694 r = end_line(dot);
695 }
696 dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
697 dot_skip_over_ws();
698 } else if (strncasecmp((char *) cmd, "edit", i) == 0) { // Edit a file
699 int sr;
700 sr= 0;
701 // don't edit, if the current file has been modified
702 if (file_modified && ! useforce) {
703 psbs("No write since last change (:edit! overrides)");
704 goto vc1;
705 }
706 if (strlen((char*)args) > 0) {
707 // the user supplied a file name
708 fn= args;
709 } else if (cfn != 0 && strlen((char*)cfn) > 0) {
710 // no user supplied name- use the current filename
711 fn= cfn;
712 goto vc5;
713 } else {
714 // no user file name, no current name- punt
715 psbs("No current filename");
716 goto vc1;
717 }
718
719 // see if file exists- if not, its just a new file request
720 if ((sr=stat((char*)fn, &st_buf)) < 0) {
721 // This is just a request for a new file creation.
722 // The file_insert below will fail but we get
723 // an empty buffer with a file name. Then the "write"
724 // command can do the create.
725 } else {
726 if ((st_buf.st_mode & (S_IFREG)) == 0) {
727 // This is not a regular file
728 psbs("\"%s\" is not a regular file", fn);
729 goto vc1;
730 }
731 if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
732 // dont have any read permissions
733 psbs("\"%s\" is not readable", fn);
734 goto vc1;
735 }
736 }
737
738 // There is a read-able regular file
739 // make this the current file
740 q = (Byte *) bb_xstrdup((char *) fn); // save the cfn
741 free(cfn); // free the old name
742 cfn = q; // remember new cfn
743
744 vc5:
745 // delete all the contents of text[]
746 new_text(2 * file_size(fn));
747 screenbegin = dot = end = text;
748
749 // insert new file
750 ch = file_insert(fn, text, file_size(fn));
751
752 if (ch < 1) {
753 // start empty buf with dummy line
754 (void) char_insert(text, '\n');
755 ch= 1;
756 }
757 file_modified = 0;
758 last_file_modified = -1;
759#ifdef CONFIG_FEATURE_VI_YANKMARK
760 if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
761 free(reg[Ureg]); // free orig line reg- for 'U'
762 reg[Ureg]= 0;
763 }
764 if (YDreg >= 0 && YDreg < 28 && reg[YDreg] != 0) {
765 free(reg[YDreg]); // free default yank/delete register
766 reg[YDreg]= 0;
767 }
768 for (li = 0; li < 28; li++) {
769 mark[li] = 0;
770 } // init the marks
771#endif /* CONFIG_FEATURE_VI_YANKMARK */
772 // how many lines in text[]?
773 li = count_lines(text, end - 1);
774 psb("\"%s\"%s"
775#ifdef CONFIG_FEATURE_VI_READONLY
776 "%s"
777#endif /* CONFIG_FEATURE_VI_READONLY */
778 " %dL, %dC", cfn,
779 (sr < 0 ? " [New file]" : ""),
780#ifdef CONFIG_FEATURE_VI_READONLY
781 ((vi_readonly || readonly) ? " [Read only]" : ""),
782#endif /* CONFIG_FEATURE_VI_READONLY */
783 li, ch);
784 } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this
785 if (b != -1 || e != -1) {
786 ni((Byte *) "No address allowed on this command");
787 goto vc1;
788 }
789 if (strlen((char *) args) > 0) {
790 // user wants a new filename
791 free(cfn);
792 cfn = (Byte *) bb_xstrdup((char *) args);
793 } else {
794 // user wants file status info
795 last_status_cksum = 0; // force status update
796 }
797 } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available
798 // print out values of all features
799 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
800 clear_to_eol(); // clear the line
801 cookmode();
802 show_help();
803 rawmode();
804 Hit_Return();
805 } else if (strncasecmp((char *) cmd, "list", i) == 0) { // literal print line
806 if (b < 0) { // no addr given- use defaults
807 q = begin_line(dot); // assume .,. for the range
808 r = end_line(dot);
809 }
810 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
811 clear_to_eol(); // clear the line
812 puts("\r");
813 for (; q <= r; q++) {
814 int c_is_no_print;
815
816 c = *q;
817 c_is_no_print = c > 127 && !Isprint(c);
818 if (c_is_no_print) {
819 c = '.';
820 standout_start();
821 }
822 if (c == '\n') {
823 write1("$\r");
824 } else if (c < ' ' || c == 127) {
825 putchar('^');
826 if(c == 127)
827 c = '?';
828 else
829 c += '@';
830 }
831 putchar(c);
832 if (c_is_no_print)
833 standout_end();
834 }
835#ifdef CONFIG_FEATURE_VI_SET
836 vc2:
837#endif /* CONFIG_FEATURE_VI_SET */
838 Hit_Return();
839 } else if ((strncasecmp((char *) cmd, "quit", i) == 0) || // Quit
840 (strncasecmp((char *) cmd, "next", i) == 0)) { // edit next file
841 if (useforce) {
842 // force end of argv list
843 if (*cmd == 'q') {
844 optind = save_argc;
845 }
846 editing = 0;
847 goto vc1;
848 }
849 // don't exit if the file been modified
850 if (file_modified) {
851 psbs("No write since last change (:%s! overrides)",
852 (*cmd == 'q' ? "quit" : "next"));
853 goto vc1;
854 }
855 // are there other file to edit
856 if (*cmd == 'q' && optind < save_argc - 1) {
857 psbs("%d more file to edit", (save_argc - optind - 1));
858 goto vc1;
859 }
860 if (*cmd == 'n' && optind >= save_argc - 1) {
861 psbs("No more files to edit");
862 goto vc1;
863 }
864 editing = 0;
865 } else if (strncasecmp((char *) cmd, "read", i) == 0) { // read file into text[]
866 fn = args;
867 if (strlen((char *) fn) <= 0) {
868 psbs("No filename given");
869 goto vc1;
870 }
871 if (b < 0) { // no addr given- use defaults
872 q = begin_line(dot); // assume "dot"
873 }
874 // read after current line- unless user said ":0r foo"
875 if (b != 0)
876 q = next_line(q);
877#ifdef CONFIG_FEATURE_VI_READONLY
878 l= readonly; // remember current files' status
879#endif
880 ch = file_insert(fn, q, file_size(fn));
881#ifdef CONFIG_FEATURE_VI_READONLY
882 readonly= l;
883#endif
884 if (ch < 0)
885 goto vc1; // nothing was inserted
886 // how many lines in text[]?
887 li = count_lines(q, q + ch - 1);
888 psb("\"%s\""
889#ifdef CONFIG_FEATURE_VI_READONLY
890 "%s"
891#endif /* CONFIG_FEATURE_VI_READONLY */
892 " %dL, %dC", fn,
893#ifdef CONFIG_FEATURE_VI_READONLY
894 ((vi_readonly || readonly) ? " [Read only]" : ""),
895#endif /* CONFIG_FEATURE_VI_READONLY */
896 li, ch);
897 if (ch > 0) {
898 // if the insert is before "dot" then we need to update
899 if (q <= dot)
900 dot += ch;
901 file_modified++;
902 }
903 } else if (strncasecmp((char *) cmd, "rewind", i) == 0) { // rewind cmd line args
904 if (file_modified && ! useforce) {
905 psbs("No write since last change (:rewind! overrides)");
906 } else {
907 // reset the filenames to edit
908 optind = fn_start - 1;
909 editing = 0;
910 }
911#ifdef CONFIG_FEATURE_VI_SET
912 } else if (strncasecmp((char *) cmd, "set", i) == 0) { // set or clear features
913 i = 0; // offset into args
914 if (strlen((char *) args) == 0) {
915 // print out values of all options
916 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
917 clear_to_eol(); // clear the line
918 printf("----------------------------------------\r\n");
919#ifdef CONFIG_FEATURE_VI_SETOPTS
920 if (!autoindent)
921 printf("no");
922 printf("autoindent ");
923 if (!err_method)
924 printf("no");
925 printf("flash ");
926 if (!ignorecase)
927 printf("no");
928 printf("ignorecase ");
929 if (!showmatch)
930 printf("no");
931 printf("showmatch ");
932 printf("tabstop=%d ", tabstop);
933#endif /* CONFIG_FEATURE_VI_SETOPTS */
934 printf("\r\n");
935 goto vc2;
936 }
937 if (strncasecmp((char *) args, "no", 2) == 0)
938 i = 2; // ":set noautoindent"
939#ifdef CONFIG_FEATURE_VI_SETOPTS
940 setops(args, "autoindent ", i, "ai", VI_AUTOINDENT);
941 setops(args, "flash ", i, "fl", VI_ERR_METHOD);
942 setops(args, "ignorecase ", i, "ic", VI_IGNORECASE);
943 setops(args, "showmatch ", i, "ic", VI_SHOWMATCH);
944 if (strncasecmp((char *) args + i, "tabstop=%d ", 7) == 0) {
945 sscanf(strchr((char *) args + i, '='), "=%d", &ch);
946 if (ch > 0 && ch < columns - 1)
947 tabstop = ch;
948 }
949#endif /* CONFIG_FEATURE_VI_SETOPTS */
950#endif /* CONFIG_FEATURE_VI_SET */
951#ifdef CONFIG_FEATURE_VI_SEARCH
952 } else if (strncasecmp((char *) cmd, "s", 1) == 0) { // substitute a pattern with a replacement pattern
953 Byte *ls, *F, *R;
954 int gflag;
955
956 // F points to the "find" pattern
957 // R points to the "replace" pattern
958 // replace the cmd line delimiters "/" with NULLs
959 gflag = 0; // global replace flag
960 c = orig_buf[1]; // what is the delimiter
961 F = orig_buf + 2; // start of "find"
962 R = (Byte *) strchr((char *) F, c); // middle delimiter
963 if (!R) goto colon_s_fail;
964 *R++ = '\0'; // terminate "find"
965 buf1 = (Byte *) strchr((char *) R, c);
966 if (!buf1) goto colon_s_fail;
967 *buf1++ = '\0'; // terminate "replace"
968 if (*buf1 == 'g') { // :s/foo/bar/g
969 buf1++;
970 gflag++; // turn on gflag
971 }
972 q = begin_line(q);
973 if (b < 0) { // maybe :s/foo/bar/
974 q = begin_line(dot); // start with cur line
975 b = count_lines(text, q); // cur line number
976 }
977 if (e < 0)
978 e = b; // maybe :.s/foo/bar/
979 for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
980 ls = q; // orig line start
981 vc4:
982 buf1 = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
983 if (buf1 != NULL) {
984 // we found the "find" pattern- delete it
985 (void) text_hole_delete(buf1, buf1 + strlen((char *) F) - 1);
986 // inset the "replace" patern
987 (void) string_insert(buf1, R); // insert the string
988 // check for "global" :s/foo/bar/g
989 if (gflag == 1) {
990 if ((buf1 + strlen((char *) R)) < end_line(ls)) {
991 q = buf1 + strlen((char *) R);
992 goto vc4; // don't let q move past cur line
993 }
994 }
995 }
996 q = next_line(ls);
997 }
998#endif /* CONFIG_FEATURE_VI_SEARCH */
999 } else if (strncasecmp((char *) cmd, "version", i) == 0) { // show software version
1000 psb("%s", vi_Version);
1001 } else if (strncasecmp((char *) cmd, "write", i) == 0 // write text to file
1002 || strncasecmp((char *) cmd, "wq", i) == 0
1003 || strncasecmp((char *) cmd, "wn", i) == 0
1004 || strncasecmp((char *) cmd, "x", i) == 0) {
1005 // is there a file name to write to?
1006 if (strlen((char *) args) > 0) {
1007 fn = args;
1008 }
1009#ifdef CONFIG_FEATURE_VI_READONLY
1010 if ((vi_readonly || readonly) && ! useforce) {
1011 psbs("\"%s\" File is read only", fn);
1012 goto vc3;
1013 }
1014#endif /* CONFIG_FEATURE_VI_READONLY */
1015 // how many lines in text[]?
1016 li = count_lines(q, r);
1017 ch = r - q + 1;
1018 // see if file exists- if not, its just a new file request
1019 if (useforce) {
1020 // if "fn" is not write-able, chmod u+w
1021 // sprintf(syscmd, "chmod u+w %s", fn);
1022 // system(syscmd);
1023 forced = TRUE;
1024 }
1025 l = file_write(fn, q, r);
1026 if (useforce && forced) {
1027 // chmod u-w
1028 // sprintf(syscmd, "chmod u-w %s", fn);
1029 // system(syscmd);
1030 forced = FALSE;
1031 }
1032 if (l < 0) {
1033 if (l == -1)
1034 psbs("Write error: %s", strerror(errno));
1035 } else {
1036 psb("\"%s\" %dL, %dC", fn, li, l);
1037 if (q == text && r == end - 1 && l == ch) {
1038 file_modified = 0;
1039 last_file_modified = -1;
1040 }
1041 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' ||
1042 cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N')
1043 && l == ch) {
1044 editing = 0;
1045 }
1046 }
1047#ifdef CONFIG_FEATURE_VI_READONLY
1048 vc3:;
1049#endif /* CONFIG_FEATURE_VI_READONLY */
1050#ifdef CONFIG_FEATURE_VI_YANKMARK
1051 } else if (strncasecmp((char *) cmd, "yank", i) == 0) { // yank lines
1052 if (b < 0) { // no addr given- use defaults
1053 q = begin_line(dot); // assume .,. for the range
1054 r = end_line(dot);
1055 }
1056 text_yank(q, r, YDreg);
1057 li = count_lines(q, r);
1058 psb("Yank %d lines (%d chars) into [%c]",
1059 li, strlen((char *) reg[YDreg]), what_reg());
1060#endif /* CONFIG_FEATURE_VI_YANKMARK */
1061 } else {
1062 // cmd unknown
1063 ni((Byte *) cmd);
1064 }
1065 vc1:
1066 dot = bound_dot(dot); // make sure "dot" is valid
1067 return;
1068#ifdef CONFIG_FEATURE_VI_SEARCH
1069colon_s_fail:
1070 psb(":s expression missing delimiters");
1071#endif
1072}
1073
1074#endif /* CONFIG_FEATURE_VI_COLON */
1075
1076static void Hit_Return(void)
1077{
1078 char c;
1079
1080 standout_start(); // start reverse video
1081 write1("[Hit return to continue]");
1082 standout_end(); // end reverse video
1083 while ((c = get_one_char()) != '\n' && c != '\r') /*do nothing */
1084 ;
1085 redraw(TRUE); // force redraw all
1086}
1087
1088//----- Synchronize the cursor to Dot --------------------------
1089static void sync_cursor(Byte * d, int *row, int *col)
1090{
1091 Byte *beg_cur; // begin and end of "d" line
1092 Byte *beg_scr, *end_scr; // begin and end of screen
1093 Byte *tp;
1094 int cnt, ro, co;
1095
1096 beg_cur = begin_line(d); // first char of cur line
1097
1098 beg_scr = end_scr = screenbegin; // first char of screen
1099 end_scr = end_screen(); // last char of screen
1100
1101 if (beg_cur < screenbegin) {
1102 // "d" is before top line on screen
1103 // how many lines do we have to move
1104 cnt = count_lines(beg_cur, screenbegin);
1105 sc1:
1106 screenbegin = beg_cur;
1107 if (cnt > (rows - 1) / 2) {
1108 // we moved too many lines. put "dot" in middle of screen
1109 for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
1110 screenbegin = prev_line(screenbegin);
1111 }
1112 }
1113 } else if (beg_cur > end_scr) {
1114 // "d" is after bottom line on screen
1115 // how many lines do we have to move
1116 cnt = count_lines(end_scr, beg_cur);
1117 if (cnt > (rows - 1) / 2)
1118 goto sc1; // too many lines
1119 for (ro = 0; ro < cnt - 1; ro++) {
1120 // move screen begin the same amount
1121 screenbegin = next_line(screenbegin);
1122 // now, move the end of screen
1123 end_scr = next_line(end_scr);
1124 end_scr = end_line(end_scr);
1125 }
1126 }
1127 // "d" is on screen- find out which row
1128 tp = screenbegin;
1129 for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
1130 if (tp == beg_cur)
1131 break;
1132 tp = next_line(tp);
1133 }
1134
1135 // find out what col "d" is on
1136 co = 0;
1137 do { // drive "co" to correct column
1138 if (*tp == '\n' || *tp == '\0')
1139 break;
1140 if (*tp == '\t') {
1141 // 7 - (co % 8 )
1142 co += ((tabstop - 1) - (co % tabstop));
1143 } else if (*tp < ' ' || *tp == 127) {
1144 co++; // display as ^X, use 2 columns
1145 }
1146 } while (tp++ < d && ++co);
1147
1148 // "co" is the column where "dot" is.
1149 // The screen has "columns" columns.
1150 // The currently displayed columns are 0+offset -- columns+ofset
1151 // |-------------------------------------------------------------|
1152 // ^ ^ ^
1153 // offset | |------- columns ----------------|
1154 //
1155 // If "co" is already in this range then we do not have to adjust offset
1156 // but, we do have to subtract the "offset" bias from "co".
1157 // If "co" is outside this range then we have to change "offset".
1158 // If the first char of a line is a tab the cursor will try to stay
1159 // in column 7, but we have to set offset to 0.
1160
1161 if (co < 0 + offset) {
1162 offset = co;
1163 }
1164 if (co >= columns + offset) {
1165 offset = co - columns + 1;
1166 }
1167 // if the first char of the line is a tab, and "dot" is sitting on it
1168 // force offset to 0.
1169 if (d == beg_cur && *d == '\t') {
1170 offset = 0;
1171 }
1172 co -= offset;
1173
1174 *row = ro;
1175 *col = co;
1176}
1177
1178//----- Text Movement Routines ---------------------------------
1179static Byte *begin_line(Byte * p) // return pointer to first char cur line
1180{
1181 while (p > text && p[-1] != '\n')
1182 p--; // go to cur line B-o-l
1183 return (p);
1184}
1185
1186static Byte *end_line(Byte * p) // return pointer to NL of cur line line
1187{
1188 while (p < end - 1 && *p != '\n')
1189 p++; // go to cur line E-o-l
1190 return (p);
1191}
1192
1193static inline Byte *dollar_line(Byte * p) // return pointer to just before NL line
1194{
1195 while (p < end - 1 && *p != '\n')
1196 p++; // go to cur line E-o-l
1197 // Try to stay off of the Newline
1198 if (*p == '\n' && (p - begin_line(p)) > 0)
1199 p--;
1200 return (p);
1201}
1202
1203static Byte *prev_line(Byte * p) // return pointer first char prev line
1204{
1205 p = begin_line(p); // goto begining of cur line
1206 if (p[-1] == '\n' && p > text)
1207 p--; // step to prev line
1208 p = begin_line(p); // goto begining of prev line
1209 return (p);
1210}
1211
1212static Byte *next_line(Byte * p) // return pointer first char next line
1213{
1214 p = end_line(p);
1215 if (*p == '\n' && p < end - 1)
1216 p++; // step to next line
1217 return (p);
1218}
1219
1220//----- Text Information Routines ------------------------------
1221static Byte *end_screen(void)
1222{
1223 Byte *q;
1224 int cnt;
1225
1226 // find new bottom line
1227 q = screenbegin;
1228 for (cnt = 0; cnt < rows - 2; cnt++)
1229 q = next_line(q);
1230 q = end_line(q);
1231 return (q);
1232}
1233
1234static int count_lines(Byte * start, Byte * stop) // count line from start to stop
1235{
1236 Byte *q;
1237 int cnt;
1238
1239 if (stop < start) { // start and stop are backwards- reverse them
1240 q = start;
1241 start = stop;
1242 stop = q;
1243 }
1244 cnt = 0;
1245 stop = end_line(stop); // get to end of this line
1246 for (q = start; q <= stop && q <= end - 1; q++) {
1247 if (*q == '\n')
1248 cnt++;
1249 }
1250 return (cnt);
1251}
1252
1253static Byte *find_line(int li) // find begining of line #li
1254{
1255 Byte *q;
1256
1257 for (q = text; li > 1; li--) {
1258 q = next_line(q);
1259 }
1260 return (q);
1261}
1262
1263//----- Dot Movement Routines ----------------------------------
1264static void dot_left(void)
1265{
1266 if (dot > text && dot[-1] != '\n')
1267 dot--;
1268}
1269
1270static void dot_right(void)
1271{
1272 if (dot < end - 1 && *dot != '\n')
1273 dot++;
1274}
1275
1276static void dot_begin(void)
1277{
1278 dot = begin_line(dot); // return pointer to first char cur line
1279}
1280
1281static void dot_end(void)
1282{
1283 dot = end_line(dot); // return pointer to last char cur line
1284}
1285
1286static Byte *move_to_col(Byte * p, int l)
1287{
1288 int co;
1289
1290 p = begin_line(p);
1291 co = 0;
1292 do {
1293 if (*p == '\n' || *p == '\0')
1294 break;
1295 if (*p == '\t') {
1296 // 7 - (co % 8 )
1297 co += ((tabstop - 1) - (co % tabstop));
1298 } else if (*p < ' ' || *p == 127) {
1299 co++; // display as ^X, use 2 columns
1300 }
1301 } while (++co <= l && p++ < end);
1302 return (p);
1303}
1304
1305static void dot_next(void)
1306{
1307 dot = next_line(dot);
1308}
1309
1310static void dot_prev(void)
1311{
1312 dot = prev_line(dot);
1313}
1314
1315static void dot_scroll(int cnt, int dir)
1316{
1317 Byte *q;
1318
1319 for (; cnt > 0; cnt--) {
1320 if (dir < 0) {
1321 // scroll Backwards
1322 // ctrl-Y scroll up one line
1323 screenbegin = prev_line(screenbegin);
1324 } else {
1325 // scroll Forwards
1326 // ctrl-E scroll down one line
1327 screenbegin = next_line(screenbegin);
1328 }
1329 }
1330 // make sure "dot" stays on the screen so we dont scroll off
1331 if (dot < screenbegin)
1332 dot = screenbegin;
1333 q = end_screen(); // find new bottom line
1334 if (dot > q)
1335 dot = begin_line(q); // is dot is below bottom line?
1336 dot_skip_over_ws();
1337}
1338
1339static void dot_skip_over_ws(void)
1340{
1341 // skip WS
1342 while (isspace(*dot) && *dot != '\n' && dot < end - 1)
1343 dot++;
1344}
1345
1346static void dot_delete(void) // delete the char at 'dot'
1347{
1348 (void) text_hole_delete(dot, dot);
1349}
1350
1351static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end"
1352{
1353 if (p >= end && end > text) {
1354 p = end - 1;
1355 indicate_error('1');
1356 }
1357 if (p < text) {
1358 p = text;
1359 indicate_error('2');
1360 }
1361 return (p);
1362}
1363
1364//----- Helper Utility Routines --------------------------------
1365
1366//----------------------------------------------------------------
1367//----- Char Routines --------------------------------------------
1368/* Chars that are part of a word-
1369 * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
1370 * Chars that are Not part of a word (stoppers)
1371 * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
1372 * Chars that are WhiteSpace
1373 * TAB NEWLINE VT FF RETURN SPACE
1374 * DO NOT COUNT NEWLINE AS WHITESPACE
1375 */
1376
1377static Byte *new_screen(int ro, int co)
1378{
1379 int li;
1380
1381 free(screen);
1382 screensize = ro * co + 8;
1383 screen = (Byte *) xmalloc(screensize);
1384 // initialize the new screen. assume this will be a empty file.
1385 screen_erase();
1386 // non-existent text[] lines start with a tilde (~).
1387 for (li = 1; li < ro - 1; li++) {
1388 screen[(li * co) + 0] = '~';
1389 }
1390 return (screen);
1391}
1392
1393static Byte *new_text(int size)
1394{
1395 if (size < 10240)
1396 size = 10240; // have a minimum size for new files
1397 free(text);
1398 text = (Byte *) xmalloc(size + 8);
1399 memset(text, '\0', size); // clear new text[]
1400 //text += 4; // leave some room for "oops"
1401 textend = text + size - 1;
1402 //textend -= 4; // leave some root for "oops"
1403 return (text);
1404}
1405
1406#ifdef CONFIG_FEATURE_VI_SEARCH
1407static int mycmp(Byte * s1, Byte * s2, int len)
1408{
1409 int i;
1410
1411 i = strncmp((char *) s1, (char *) s2, len);
1412#ifdef CONFIG_FEATURE_VI_SETOPTS
1413 if (ignorecase) {
1414 i = strncasecmp((char *) s1, (char *) s2, len);
1415 }
1416#endif /* CONFIG_FEATURE_VI_SETOPTS */
1417 return (i);
1418}
1419
1420static Byte *char_search(Byte * p, Byte * pat, int dir, int range) // search for pattern starting at p
1421{
1422#ifndef REGEX_SEARCH
1423 Byte *start, *stop;
1424 int len;
1425
1426 len = strlen((char *) pat);
1427 if (dir == FORWARD) {
1428 stop = end - 1; // assume range is p - end-1
1429 if (range == LIMITED)
1430 stop = next_line(p); // range is to next line
1431 for (start = p; start < stop; start++) {
1432 if (mycmp(start, pat, len) == 0) {
1433 return (start);
1434 }
1435 }
1436 } else if (dir == BACK) {
1437 stop = text; // assume range is text - p
1438 if (range == LIMITED)
1439 stop = prev_line(p); // range is to prev line
1440 for (start = p - len; start >= stop; start--) {
1441 if (mycmp(start, pat, len) == 0) {
1442 return (start);
1443 }
1444 }
1445 }
1446 // pattern not found
1447 return (NULL);
1448#else /*REGEX_SEARCH */
1449 char *q;
1450 struct re_pattern_buffer preg;
1451 int i;
1452 int size, range;
1453
1454 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
1455 preg.translate = 0;
1456 preg.fastmap = 0;
1457 preg.buffer = 0;
1458 preg.allocated = 0;
1459
1460 // assume a LIMITED forward search
1461 q = next_line(p);
1462 q = end_line(q);
1463 q = end - 1;
1464 if (dir == BACK) {
1465 q = prev_line(p);
1466 q = text;
1467 }
1468 // count the number of chars to search over, forward or backward
1469 size = q - p;
1470 if (size < 0)
1471 size = p - q;
1472 // RANGE could be negative if we are searching backwards
1473 range = q - p;
1474
1475 q = (char *) re_compile_pattern(pat, strlen((char *) pat), &preg);
1476 if (q != 0) {
1477 // The pattern was not compiled
1478 psbs("bad search pattern: \"%s\": %s", pat, q);
1479 i = 0; // return p if pattern not compiled
1480 goto cs1;
1481 }
1482
1483 q = p;
1484 if (range < 0) {
1485 q = p - size;
1486 if (q < text)
1487 q = text;
1488 }
1489 // search for the compiled pattern, preg, in p[]
1490 // range < 0- search backward
1491 // range > 0- search forward
1492 // 0 < start < size
1493 // re_search() < 0 not found or error
1494 // re_search() > 0 index of found pattern
1495 // struct pattern char int int int struct reg
1496 // re_search (*pattern_buffer, *string, size, start, range, *regs)
1497 i = re_search(&preg, q, size, 0, range, 0);
1498 if (i == -1) {
1499 p = 0;
1500 i = 0; // return NULL if pattern not found
1501 }
1502 cs1:
1503 if (dir == FORWARD) {
1504 p = p + i;
1505 } else {
1506 p = p - i;
1507 }
1508 return (p);
1509#endif /*REGEX_SEARCH */
1510}
1511#endif /* CONFIG_FEATURE_VI_SEARCH */
1512
1513static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p'
1514{
1515 if (c == 22) { // Is this an ctrl-V?
1516 p = stupid_insert(p, '^'); // use ^ to indicate literal next
1517 p--; // backup onto ^
1518 refresh(FALSE); // show the ^
1519 c = get_one_char();
1520 *p = c;
1521 p++;
1522 file_modified++; // has the file been modified
1523 } else if (c == 27) { // Is this an ESC?
1524 cmd_mode = 0;
1525 cmdcnt = 0;
1526 end_cmd_q(); // stop adding to q
1527 last_status_cksum = 0; // force status update
1528 if ((p[-1] != '\n') && (dot>text)) {
1529 p--;
1530 }
1531 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
1532 // 123456789
1533 if ((p[-1] != '\n') && (dot>text)) {
1534 p--;
1535 p = text_hole_delete(p, p); // shrink buffer 1 char
1536 }
1537 } else {
1538 // insert a char into text[]
1539 Byte *sp; // "save p"
1540
1541 if (c == 13)
1542 c = '\n'; // translate \r to \n
1543 sp = p; // remember addr of insert
1544 p = stupid_insert(p, c); // insert the char
1545#ifdef CONFIG_FEATURE_VI_SETOPTS
1546 if (showmatch && strchr(")]}", *sp) != NULL) {
1547 showmatching(sp);
1548 }
1549 if (autoindent && c == '\n') { // auto indent the new line
1550 Byte *q;
1551
1552 q = prev_line(p); // use prev line as templet
1553 for (; isblnk(*q); q++) {
1554 p = stupid_insert(p, *q); // insert the char
1555 }
1556 }
1557#endif /* CONFIG_FEATURE_VI_SETOPTS */
1558 }
1559 return (p);
1560}
1561
1562static Byte *stupid_insert(Byte * p, Byte c) // stupidly insert the char c at 'p'
1563{
1564 p = text_hole_make(p, 1);
1565 if (p != 0) {
1566 *p = c;
1567 file_modified++; // has the file been modified
1568 p++;
1569 }
1570 return (p);
1571}
1572
1573static Byte find_range(Byte ** start, Byte ** stop, Byte c)
1574{
1575 Byte *save_dot, *p, *q;
1576 int cnt;
1577
1578 save_dot = dot;
1579 p = q = dot;
1580
1581 if (strchr("cdy><", c)) {
1582 // these cmds operate on whole lines
1583 p = q = begin_line(p);
1584 for (cnt = 1; cnt < cmdcnt; cnt++) {
1585 q = next_line(q);
1586 }
1587 q = end_line(q);
1588 } else if (strchr("^%$0bBeEft", c)) {
1589 // These cmds operate on char positions
1590 do_cmd(c); // execute movement cmd
1591 q = dot;
1592 } else if (strchr("wW", c)) {
1593 do_cmd(c); // execute movement cmd
1594 // if we are at the next word's first char
1595 // step back one char
1596 // but check the possibilities when it is true
1597 if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
1598 || (ispunct(dot[-1]) && !ispunct(dot[0]))
1599 || (isalnum(dot[-1]) && !isalnum(dot[0]))))
1600 dot--; // move back off of next word
1601 if (dot > text && *dot == '\n')
1602 dot--; // stay off NL
1603 q = dot;
1604 } else if (strchr("H-k{", c)) {
1605 // these operate on multi-lines backwards
1606 q = end_line(dot); // find NL
1607 do_cmd(c); // execute movement cmd
1608 dot_begin();
1609 p = dot;
1610 } else if (strchr("L+j}\r\n", c)) {
1611 // these operate on multi-lines forwards
1612 p = begin_line(dot);
1613 do_cmd(c); // execute movement cmd
1614 dot_end(); // find NL
1615 q = dot;
1616 } else {
1617 c = 27; // error- return an ESC char
1618 //break;
1619 }
1620 *start = p;
1621 *stop = q;
1622 if (q < p) {
1623 *start = q;
1624 *stop = p;
1625 }
1626 dot = save_dot;
1627 return (c);
1628}
1629
1630static int st_test(Byte * p, int type, int dir, Byte * tested)
1631{
1632 Byte c, c0, ci;
1633 int test, inc;
1634
1635 inc = dir;
1636 c = c0 = p[0];
1637 ci = p[inc];
1638 test = 0;
1639
1640 if (type == S_BEFORE_WS) {
1641 c = ci;
1642 test = ((!isspace(c)) || c == '\n');
1643 }
1644 if (type == S_TO_WS) {
1645 c = c0;
1646 test = ((!isspace(c)) || c == '\n');
1647 }
1648 if (type == S_OVER_WS) {
1649 c = c0;
1650 test = ((isspace(c)));
1651 }
1652 if (type == S_END_PUNCT) {
1653 c = ci;
1654 test = ((ispunct(c)));
1655 }
1656 if (type == S_END_ALNUM) {
1657 c = ci;
1658 test = ((isalnum(c)) || c == '_');
1659 }
1660 *tested = c;
1661 return (test);
1662}
1663
1664static Byte *skip_thing(Byte * p, int linecnt, int dir, int type)
1665{
1666 Byte c;
1667
1668 while (st_test(p, type, dir, &c)) {
1669 // make sure we limit search to correct number of lines
1670 if (c == '\n' && --linecnt < 1)
1671 break;
1672 if (dir >= 0 && p >= end - 1)
1673 break;
1674 if (dir < 0 && p <= text)
1675 break;
1676 p += dir; // move to next char
1677 }
1678 return (p);
1679}
1680
1681// find matching char of pair () [] {}
1682static Byte *find_pair(Byte * p, Byte c)
1683{
1684 Byte match, *q;
1685 int dir, level;
1686
1687 match = ')';
1688 level = 1;
1689 dir = 1; // assume forward
1690 switch (c) {
1691 case '(':
1692 match = ')';
1693 break;
1694 case '[':
1695 match = ']';
1696 break;
1697 case '{':
1698 match = '}';
1699 break;
1700 case ')':
1701 match = '(';
1702 dir = -1;
1703 break;
1704 case ']':
1705 match = '[';
1706 dir = -1;
1707 break;
1708 case '}':
1709 match = '{';
1710 dir = -1;
1711 break;
1712 }
1713 for (q = p + dir; text <= q && q < end; q += dir) {
1714 // look for match, count levels of pairs (( ))
1715 if (*q == c)
1716 level++; // increase pair levels
1717 if (*q == match)
1718 level--; // reduce pair level
1719 if (level == 0)
1720 break; // found matching pair
1721 }
1722 if (level != 0)
1723 q = NULL; // indicate no match
1724 return (q);
1725}
1726
1727#ifdef CONFIG_FEATURE_VI_SETOPTS
1728// show the matching char of a pair, () [] {}
1729static void showmatching(Byte * p)
1730{
1731 Byte *q, *save_dot;
1732
1733 // we found half of a pair
1734 q = find_pair(p, *p); // get loc of matching char
1735 if (q == NULL) {
1736 indicate_error('3'); // no matching char
1737 } else {
1738 // "q" now points to matching pair
1739 save_dot = dot; // remember where we are
1740 dot = q; // go to new loc
1741 refresh(FALSE); // let the user see it
1742 (void) mysleep(40); // give user some time
1743 dot = save_dot; // go back to old loc
1744 refresh(FALSE);
1745 }
1746}
1747#endif /* CONFIG_FEATURE_VI_SETOPTS */
1748
1749// open a hole in text[]
1750static Byte *text_hole_make(Byte * p, int size) // at "p", make a 'size' byte hole
1751{
1752 Byte *src, *dest;
1753 int cnt;
1754
1755 if (size <= 0)
1756 goto thm0;
1757 src = p;
1758 dest = p + size;
1759 cnt = end - src; // the rest of buffer
1760 if (memmove(dest, src, cnt) != dest) {
1761 psbs("can't create room for new characters");
1762 }
1763 memset(p, ' ', size); // clear new hole
1764 end = end + size; // adjust the new END
1765 file_modified++; // has the file been modified
1766 thm0:
1767 return (p);
1768}
1769
1770// close a hole in text[]
1771static Byte *text_hole_delete(Byte * p, Byte * q) // delete "p" thru "q", inclusive
1772{
1773 Byte *src, *dest;
1774 int cnt, hole_size;
1775
1776 // move forwards, from beginning
1777 // assume p <= q
1778 src = q + 1;
1779 dest = p;
1780 if (q < p) { // they are backward- swap them
1781 src = p + 1;
1782 dest = q;
1783 }
1784 hole_size = q - p + 1;
1785 cnt = end - src;
1786 if (src < text || src > end)
1787 goto thd0;
1788 if (dest < text || dest >= end)
1789 goto thd0;
1790 if (src >= end)
1791 goto thd_atend; // just delete the end of the buffer
1792 if (memmove(dest, src, cnt) != dest) {
1793 psbs("can't delete the character");
1794 }
1795 thd_atend:
1796 end = end - hole_size; // adjust the new END
1797 if (dest >= end)
1798 dest = end - 1; // make sure dest in below end-1
1799 if (end <= text)
1800 dest = end = text; // keep pointers valid
1801 file_modified++; // has the file been modified
1802 thd0:
1803 return (dest);
1804}
1805
1806// copy text into register, then delete text.
1807// if dist <= 0, do not include, or go past, a NewLine
1808//
1809static Byte *yank_delete(Byte * start, Byte * stop, int dist, int yf)
1810{
1811 Byte *p;
1812
1813 // make sure start <= stop
1814 if (start > stop) {
1815 // they are backwards, reverse them
1816 p = start;
1817 start = stop;
1818 stop = p;
1819 }
1820 if (dist <= 0) {
1821 // we can not cross NL boundaries
1822 p = start;
1823 if (*p == '\n')
1824 return (p);
1825 // dont go past a NewLine
1826 for (; p + 1 <= stop; p++) {
1827 if (p[1] == '\n') {
1828 stop = p; // "stop" just before NewLine
1829 break;
1830 }
1831 }
1832 }
1833 p = start;
1834#ifdef CONFIG_FEATURE_VI_YANKMARK
1835 text_yank(start, stop, YDreg);
1836#endif /* CONFIG_FEATURE_VI_YANKMARK */
1837 if (yf == YANKDEL) {
1838 p = text_hole_delete(start, stop);
1839 } // delete lines
1840 return (p);
1841}
1842
1843static void show_help(void)
1844{
1845 puts("These features are available:"
1846#ifdef CONFIG_FEATURE_VI_SEARCH
1847 "\n\tPattern searches with / and ?"
1848#endif /* CONFIG_FEATURE_VI_SEARCH */
1849#ifdef CONFIG_FEATURE_VI_DOT_CMD
1850 "\n\tLast command repeat with \'.\'"
1851#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1852#ifdef CONFIG_FEATURE_VI_YANKMARK
1853 "\n\tLine marking with 'x"
1854 "\n\tNamed buffers with \"x"
1855#endif /* CONFIG_FEATURE_VI_YANKMARK */
1856#ifdef CONFIG_FEATURE_VI_READONLY
1857 "\n\tReadonly if vi is called as \"view\""
1858 "\n\tReadonly with -R command line arg"
1859#endif /* CONFIG_FEATURE_VI_READONLY */
1860#ifdef CONFIG_FEATURE_VI_SET
1861 "\n\tSome colon mode commands with \':\'"
1862#endif /* CONFIG_FEATURE_VI_SET */
1863#ifdef CONFIG_FEATURE_VI_SETOPTS
1864 "\n\tSettable options with \":set\""
1865#endif /* CONFIG_FEATURE_VI_SETOPTS */
1866#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
1867 "\n\tSignal catching- ^C"
1868 "\n\tJob suspend and resume with ^Z"
1869#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
1870#ifdef CONFIG_FEATURE_VI_WIN_RESIZE
1871 "\n\tAdapt to window re-sizes"
1872#endif /* CONFIG_FEATURE_VI_WIN_RESIZE */
1873 );
1874}
1875
1876static inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable
1877{
1878 Byte c, b[2];
1879
1880 b[1] = '\0';
1881 strcpy((char *) buf, ""); // init buf
1882 if (strlen((char *) s) <= 0)
1883 s = (Byte *) "(NULL)";
1884 for (; *s > '\0'; s++) {
1885 int c_is_no_print;
1886
1887 c = *s;
1888 c_is_no_print = c > 127 && !Isprint(c);
1889 if (c_is_no_print) {
1890 strcat((char *) buf, SOn);
1891 c = '.';
1892 }
1893 if (c < ' ' || c == 127) {
1894 strcat((char *) buf, "^");
1895 if(c == 127)
1896 c = '?';
1897 else
1898 c += '@';
1899 }
1900 b[0] = c;
1901 strcat((char *) buf, (char *) b);
1902 if (c_is_no_print)
1903 strcat((char *) buf, SOs);
1904 if (*s == '\n') {
1905 strcat((char *) buf, "$");
1906 }
1907 }
1908}
1909
1910#ifdef CONFIG_FEATURE_VI_DOT_CMD
1911static void start_new_cmd_q(Byte c)
1912{
1913 // release old cmd
1914 free(last_modifying_cmd);
1915 // get buffer for new cmd
1916 last_modifying_cmd = (Byte *) xmalloc(BUFSIZ);
1917 memset(last_modifying_cmd, '\0', BUFSIZ); // clear new cmd queue
1918 // if there is a current cmd count put it in the buffer first
1919 if (cmdcnt > 0)
1920 sprintf((char *) last_modifying_cmd, "%d%c", cmdcnt, c);
1921 else // just save char c onto queue
1922 last_modifying_cmd[0] = c;
1923 adding2q = 1;
1924}
1925
1926static void end_cmd_q(void)
1927{
1928#ifdef CONFIG_FEATURE_VI_YANKMARK
1929 YDreg = 26; // go back to default Yank/Delete reg
1930#endif /* CONFIG_FEATURE_VI_YANKMARK */
1931 adding2q = 0;
1932 return;
1933}
1934#endif /* CONFIG_FEATURE_VI_DOT_CMD */
1935
1936#if defined(CONFIG_FEATURE_VI_YANKMARK) || (defined(CONFIG_FEATURE_VI_COLON) && defined(CONFIG_FEATURE_VI_SEARCH)) || defined(CONFIG_FEATURE_VI_CRASHME)
1937static Byte *string_insert(Byte * p, Byte * s) // insert the string at 'p'
1938{
1939 int cnt, i;
1940
1941 i = strlen((char *) s);
1942 p = text_hole_make(p, i);
1943 strncpy((char *) p, (char *) s, i);
1944 for (cnt = 0; *s != '\0'; s++) {
1945 if (*s == '\n')
1946 cnt++;
1947 }
1948#ifdef CONFIG_FEATURE_VI_YANKMARK
1949 psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
1950#endif /* CONFIG_FEATURE_VI_YANKMARK */
1951 return (p);
1952}
1953#endif /* CONFIG_FEATURE_VI_YANKMARK || CONFIG_FEATURE_VI_COLON || CONFIG_FEATURE_VI_CRASHME */
1954
1955#ifdef CONFIG_FEATURE_VI_YANKMARK
1956static Byte *text_yank(Byte * p, Byte * q, int dest) // copy text into a register
1957{
1958 Byte *t;
1959 int cnt;
1960
1961 if (q < p) { // they are backwards- reverse them
1962 t = q;
1963 q = p;
1964 p = t;
1965 }
1966 cnt = q - p + 1;
1967 t = reg[dest];
1968 free(t); // if already a yank register, free it
1969 t = (Byte *) xmalloc(cnt + 1); // get a new register
1970 memset(t, '\0', cnt + 1); // clear new text[]
1971 strncpy((char *) t, (char *) p, cnt); // copy text[] into bufer
1972 reg[dest] = t;
1973 return (p);
1974}
1975
1976static Byte what_reg(void)
1977{
1978 Byte c;
1979 int i;
1980
1981 i = 0;
1982 c = 'D'; // default to D-reg
1983 if (0 <= YDreg && YDreg <= 25)
1984 c = 'a' + (Byte) YDreg;
1985 if (YDreg == 26)
1986 c = 'D';
1987 if (YDreg == 27)
1988 c = 'U';
1989 return (c);
1990}
1991
1992static void check_context(Byte cmd)
1993{
1994 // A context is defined to be "modifying text"
1995 // Any modifying command establishes a new context.
1996
1997 if (dot < context_start || dot > context_end) {
1998 if (strchr((char *) modifying_cmds, cmd) != NULL) {
1999 // we are trying to modify text[]- make this the current context
2000 mark[27] = mark[26]; // move cur to prev
2001 mark[26] = dot; // move local to cur
2002 context_start = prev_line(prev_line(dot));
2003 context_end = next_line(next_line(dot));
2004 //loiter= start_loiter= now;
2005 }
2006 }
2007 return;
2008}
2009
2010static inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context
2011{
2012 Byte *tmp;
2013
2014 // the current context is in mark[26]
2015 // the previous context is in mark[27]
2016 // only swap context if other context is valid
2017 if (text <= mark[27] && mark[27] <= end - 1) {
2018 tmp = mark[27];
2019 mark[27] = mark[26];
2020 mark[26] = tmp;
2021 p = mark[26]; // where we are going- previous context
2022 context_start = prev_line(prev_line(prev_line(p)));
2023 context_end = next_line(next_line(next_line(p)));
2024 }
2025 return (p);
2026}
2027#endif /* CONFIG_FEATURE_VI_YANKMARK */
2028
2029static int isblnk(Byte c) // is the char a blank or tab
2030{
2031 return (c == ' ' || c == '\t');
2032}
2033
2034//----- Set terminal attributes --------------------------------
2035static void rawmode(void)
2036{
2037 tcgetattr(0, &term_orig);
2038 term_vi = term_orig;
2039 term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's
2040 term_vi.c_iflag &= (~IXON & ~ICRNL);
2041 term_vi.c_oflag &= (~ONLCR);
2042 term_vi.c_cc[VMIN] = 1;
2043 term_vi.c_cc[VTIME] = 0;
2044 erase_char = term_vi.c_cc[VERASE];
2045 tcsetattr(0, TCSANOW, &term_vi);
2046}
2047
2048static void cookmode(void)
2049{
2050 fflush(stdout);
2051 tcsetattr(0, TCSANOW, &term_orig);
2052}
2053
2054//----- Come here when we get a window resize signal ---------
2055#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2056static void winch_sig(int sig ATTRIBUTE_UNUSED)
2057{
2058 signal(SIGWINCH, winch_sig);
2059 if (ENABLE_FEATURE_VI_WIN_RESIZE)
2060 get_terminal_width_height(0, &columns, &rows);
2061 new_screen(rows, columns); // get memory for virtual screen
2062 redraw(TRUE); // re-draw the screen
2063}
2064
2065//----- Come here when we get a continue signal -------------------
2066static void cont_sig(int sig ATTRIBUTE_UNUSED)
2067{
2068 rawmode(); // terminal to "raw"
2069 last_status_cksum = 0; // force status update
2070 redraw(TRUE); // re-draw the screen
2071
2072 signal(SIGTSTP, suspend_sig);
2073 signal(SIGCONT, SIG_DFL);
2074 kill(my_pid, SIGCONT);
2075}
2076
2077//----- Come here when we get a Suspend signal -------------------
2078static void suspend_sig(int sig ATTRIBUTE_UNUSED)
2079{
2080 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
2081 clear_to_eol(); // Erase to end of line
2082 cookmode(); // terminal to "cooked"
2083
2084 signal(SIGCONT, cont_sig);
2085 signal(SIGTSTP, SIG_DFL);
2086 kill(my_pid, SIGTSTP);
2087}
2088
2089//----- Come here when we get a signal ---------------------------
2090static void catch_sig(int sig)
2091{
2092 signal(SIGINT, catch_sig);
2093 if(sig)
2094 longjmp(restart, sig);
2095}
2096#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2097
2098static int mysleep(int hund) // sleep for 'h' 1/100 seconds
2099{
2100 // Don't hang- Wait 5/100 seconds- 1 Sec= 1000000
2101 fflush(stdout);
2102 FD_ZERO(&rfds);
2103 FD_SET(0, &rfds);
2104 tv.tv_sec = 0;
2105 tv.tv_usec = hund * 10000;
2106 select(1, &rfds, NULL, NULL, &tv);
2107 return (FD_ISSET(0, &rfds));
2108}
2109
2110#define readbuffer bb_common_bufsiz1
2111
2112static int readed_for_parse;
2113
2114//----- IO Routines --------------------------------------------
2115static Byte readit(void) // read (maybe cursor) key from stdin
2116{
2117 Byte c;
2118 int n;
2119 struct esc_cmds {
2120 const char *seq;
2121 Byte val;
2122 };
2123
2124 static const struct esc_cmds esccmds[] = {
2125 {"OA", (Byte) VI_K_UP}, // cursor key Up
2126 {"OB", (Byte) VI_K_DOWN}, // cursor key Down
2127 {"OC", (Byte) VI_K_RIGHT}, // Cursor Key Right
2128 {"OD", (Byte) VI_K_LEFT}, // cursor key Left
2129 {"OH", (Byte) VI_K_HOME}, // Cursor Key Home
2130 {"OF", (Byte) VI_K_END}, // Cursor Key End
2131 {"[A", (Byte) VI_K_UP}, // cursor key Up
2132 {"[B", (Byte) VI_K_DOWN}, // cursor key Down
2133 {"[C", (Byte) VI_K_RIGHT}, // Cursor Key Right
2134 {"[D", (Byte) VI_K_LEFT}, // cursor key Left
2135 {"[H", (Byte) VI_K_HOME}, // Cursor Key Home
2136 {"[F", (Byte) VI_K_END}, // Cursor Key End
2137 {"[1~", (Byte) VI_K_HOME}, // Cursor Key Home
2138 {"[2~", (Byte) VI_K_INSERT}, // Cursor Key Insert
2139 {"[4~", (Byte) VI_K_END}, // Cursor Key End
2140 {"[5~", (Byte) VI_K_PAGEUP}, // Cursor Key Page Up
2141 {"[6~", (Byte) VI_K_PAGEDOWN}, // Cursor Key Page Down
2142 {"OP", (Byte) VI_K_FUN1}, // Function Key F1
2143 {"OQ", (Byte) VI_K_FUN2}, // Function Key F2
2144 {"OR", (Byte) VI_K_FUN3}, // Function Key F3
2145 {"OS", (Byte) VI_K_FUN4}, // Function Key F4
2146 {"[15~", (Byte) VI_K_FUN5}, // Function Key F5
2147 {"[17~", (Byte) VI_K_FUN6}, // Function Key F6
2148 {"[18~", (Byte) VI_K_FUN7}, // Function Key F7
2149 {"[19~", (Byte) VI_K_FUN8}, // Function Key F8
2150 {"[20~", (Byte) VI_K_FUN9}, // Function Key F9
2151 {"[21~", (Byte) VI_K_FUN10}, // Function Key F10
2152 {"[23~", (Byte) VI_K_FUN11}, // Function Key F11
2153 {"[24~", (Byte) VI_K_FUN12}, // Function Key F12
2154 {"[11~", (Byte) VI_K_FUN1}, // Function Key F1
2155 {"[12~", (Byte) VI_K_FUN2}, // Function Key F2
2156 {"[13~", (Byte) VI_K_FUN3}, // Function Key F3
2157 {"[14~", (Byte) VI_K_FUN4}, // Function Key F4
2158 };
2159
2160#define ESCCMDS_COUNT (sizeof(esccmds)/sizeof(struct esc_cmds))
2161
2162 (void) alarm(0); // turn alarm OFF while we wait for input
2163 fflush(stdout);
2164 n = readed_for_parse;
2165 // get input from User- are there already input chars in Q?
2166 if (n <= 0) {
2167 ri0:
2168 // the Q is empty, wait for a typed char
2169 n = read(0, readbuffer, BUFSIZ - 1);
2170 if (n < 0) {
2171 if (errno == EINTR)
2172 goto ri0; // interrupted sys call
2173 if (errno == EBADF)
2174 editing = 0;
2175 if (errno == EFAULT)
2176 editing = 0;
2177 if (errno == EINVAL)
2178 editing = 0;
2179 if (errno == EIO)
2180 editing = 0;
2181 errno = 0;
2182 }
2183 if(n <= 0)
2184 return 0; // error
2185 if (readbuffer[0] == 27) {
2186 // This is an ESC char. Is this Esc sequence?
2187 // Could be bare Esc key. See if there are any
2188 // more chars to read after the ESC. This would
2189 // be a Function or Cursor Key sequence.
2190 FD_ZERO(&rfds);
2191 FD_SET(0, &rfds);
2192 tv.tv_sec = 0;
2193 tv.tv_usec = 50000; // Wait 5/100 seconds- 1 Sec=1000000
2194
2195 // keep reading while there are input chars and room in buffer
2196 while (select(1, &rfds, NULL, NULL, &tv) > 0 && n <= (BUFSIZ - 5)) {
2197 // read the rest of the ESC string
2198 int r = read(0, (void *) (readbuffer + n), BUFSIZ - n);
2199 if (r > 0) {
2200 n += r;
2201 }
2202 }
2203 }
2204 readed_for_parse = n;
2205 }
2206 c = readbuffer[0];
2207 if(c == 27 && n > 1) {
2208 // Maybe cursor or function key?
2209 const struct esc_cmds *eindex;
2210
2211 for (eindex = esccmds; eindex < &esccmds[ESCCMDS_COUNT]; eindex++) {
2212 int cnt = strlen(eindex->seq);
2213
2214 if(n <= cnt)
2215 continue;
2216 if(strncmp(eindex->seq, (char *) readbuffer + 1, cnt))
2217 continue;
2218 // is a Cursor key- put derived value back into Q
2219 c = eindex->val;
2220 // for squeeze out the ESC sequence
2221 n = cnt + 1;
2222 break;
2223 }
2224 if(eindex == &esccmds[ESCCMDS_COUNT]) {
2225 /* defined ESC sequence not found, set only one ESC */
2226 n = 1;
2227 }
2228 } else {
2229 n = 1;
2230 }
2231 // remove key sequence from Q
2232 readed_for_parse -= n;
2233 memmove(readbuffer, readbuffer + n, BUFSIZ - n);
2234 (void) alarm(3); // we are done waiting for input, turn alarm ON
2235 return (c);
2236}
2237
2238//----- IO Routines --------------------------------------------
2239static Byte get_one_char(void)
2240{
2241 static Byte c;
2242
2243#ifdef CONFIG_FEATURE_VI_DOT_CMD
2244 // ! adding2q && ioq == 0 read()
2245 // ! adding2q && ioq != 0 *ioq
2246 // adding2q *last_modifying_cmd= read()
2247 if (!adding2q) {
2248 // we are not adding to the q.
2249 // but, we may be reading from a q
2250 if (ioq == 0) {
2251 // there is no current q, read from STDIN
2252 c = readit(); // get the users input
2253 } else {
2254 // there is a queue to get chars from first
2255 c = *ioq++;
2256 if (c == '\0') {
2257 // the end of the q, read from STDIN
2258 free(ioq_start);
2259 ioq_start = ioq = 0;
2260 c = readit(); // get the users input
2261 }
2262 }
2263 } else {
2264 // adding STDIN chars to q
2265 c = readit(); // get the users input
2266 if (last_modifying_cmd != 0) {
2267 int len = strlen((char *) last_modifying_cmd);
2268 if (len + 1 >= BUFSIZ) {
2269 psbs("last_modifying_cmd overrun");
2270 } else {
2271 // add new char to q
2272 last_modifying_cmd[len] = c;
2273 }
2274 }
2275 }
2276#else /* CONFIG_FEATURE_VI_DOT_CMD */
2277 c = readit(); // get the users input
2278#endif /* CONFIG_FEATURE_VI_DOT_CMD */
2279 return (c); // return the char, where ever it came from
2280}
2281
2282static Byte *get_input_line(Byte * prompt) // get input line- use "status line"
2283{
2284 Byte buf[BUFSIZ];
2285 Byte c;
2286 int i;
2287 static Byte *obufp = NULL;
2288
2289 strcpy((char *) buf, (char *) prompt);
2290 last_status_cksum = 0; // force status update
2291 place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen
2292 clear_to_eol(); // clear the line
2293 write1((char *) prompt); // write out the :, /, or ? prompt
2294
2295 for (i = strlen((char *) buf); i < BUFSIZ;) {
2296 c = get_one_char(); // read user input
2297 if (c == '\n' || c == '\r' || c == 27)
2298 break; // is this end of input
2299 if (c == erase_char || c == 8 || c == 127) {
2300 // user wants to erase prev char
2301 i--; // backup to prev char
2302 buf[i] = '\0'; // erase the char
2303 buf[i + 1] = '\0'; // null terminate buffer
2304 write1("\b \b"); // erase char on screen
2305 if (i <= 0) { // user backs up before b-o-l, exit
2306 break;
2307 }
2308 } else {
2309 buf[i] = c; // save char in buffer
2310 buf[i + 1] = '\0'; // make sure buffer is null terminated
2311 putchar(c); // echo the char back to user
2312 i++;
2313 }
2314 }
2315 refresh(FALSE);
2316 free(obufp);
2317 obufp = (Byte *) bb_xstrdup((char *) buf);
2318 return (obufp);
2319}
2320
2321static int file_size(const Byte * fn) // what is the byte size of "fn"
2322{
2323 struct stat st_buf;
2324 int cnt, sr;
2325
2326 if (fn == 0 || strlen((char *)fn) <= 0)
2327 return (-1);
2328 cnt = -1;
2329 sr = stat((char *) fn, &st_buf); // see if file exists
2330 if (sr >= 0) {
2331 cnt = (int) st_buf.st_size;
2332 }
2333 return (cnt);
2334}
2335
2336static int file_insert(Byte * fn, Byte * p, int size)
2337{
2338 int fd, cnt;
2339
2340 cnt = -1;
2341#ifdef CONFIG_FEATURE_VI_READONLY
2342 readonly = FALSE;
2343#endif /* CONFIG_FEATURE_VI_READONLY */
2344 if (fn == 0 || strlen((char*) fn) <= 0) {
2345 psbs("No filename given");
2346 goto fi0;
2347 }
2348 if (size == 0) {
2349 // OK- this is just a no-op
2350 cnt = 0;
2351 goto fi0;
2352 }
2353 if (size < 0) {
2354 psbs("Trying to insert a negative number (%d) of characters", size);
2355 goto fi0;
2356 }
2357 if (p < text || p > end) {
2358 psbs("Trying to insert file outside of memory");
2359 goto fi0;
2360 }
2361
2362 // see if we can open the file
2363#ifdef CONFIG_FEATURE_VI_READONLY
2364 if (vi_readonly) goto fi1; // do not try write-mode
2365#endif
2366 fd = open((char *) fn, O_RDWR); // assume read & write
2367 if (fd < 0) {
2368 // could not open for writing- maybe file is read only
2369#ifdef CONFIG_FEATURE_VI_READONLY
2370 fi1:
2371#endif
2372 fd = open((char *) fn, O_RDONLY); // try read-only
2373 if (fd < 0) {
2374 psbs("\"%s\" %s", fn, "could not open file");
2375 goto fi0;
2376 }
2377#ifdef CONFIG_FEATURE_VI_READONLY
2378 // got the file- read-only
2379 readonly = TRUE;
2380#endif /* CONFIG_FEATURE_VI_READONLY */
2381 }
2382 p = text_hole_make(p, size);
2383 cnt = read(fd, p, size);
2384 close(fd);
2385 if (cnt < 0) {
2386 cnt = -1;
2387 p = text_hole_delete(p, p + size - 1); // un-do buffer insert
2388 psbs("could not read file \"%s\"", fn);
2389 } else if (cnt < size) {
2390 // There was a partial read, shrink unused space text[]
2391 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
2392 psbs("could not read all of file \"%s\"", fn);
2393 }
2394 if (cnt >= size)
2395 file_modified++;
2396 fi0:
2397 return (cnt);
2398}
2399
2400static int file_write(Byte * fn, Byte * first, Byte * last)
2401{
2402 int fd, cnt, charcnt;
2403
2404 if (fn == 0) {
2405 psbs("No current filename");
2406 return (-2);
2407 }
2408 charcnt = 0;
2409 // FIXIT- use the correct umask()
2410 fd = open((char *) fn, (O_WRONLY | O_CREAT | O_TRUNC), 0664);
2411 if (fd < 0)
2412 return (-1);
2413 cnt = last - first + 1;
2414 charcnt = write(fd, first, cnt);
2415 if (charcnt == cnt) {
2416 // good write
2417 //file_modified= FALSE; // the file has not been modified
2418 } else {
2419 charcnt = 0;
2420 }
2421 close(fd);
2422 return (charcnt);
2423}
2424
2425//----- Terminal Drawing ---------------------------------------
2426// The terminal is made up of 'rows' line of 'columns' columns.
2427// classically this would be 24 x 80.
2428// screen coordinates
2429// 0,0 ... 0,79
2430// 1,0 ... 1,79
2431// . ... .
2432// . ... .
2433// 22,0 ... 22,79
2434// 23,0 ... 23,79 status line
2435//
2436
2437//----- Move the cursor to row x col (count from 0, not 1) -------
2438static void place_cursor(int row, int col, int opti)
2439{
2440 char cm1[BUFSIZ];
2441 char *cm;
2442#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2443 char cm2[BUFSIZ];
2444 Byte *screenp;
2445 // char cm3[BUFSIZ];
2446 int Rrow= last_row;
2447#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2448
2449 memset(cm1, '\0', BUFSIZ - 1); // clear the buffer
2450
2451 if (row < 0) row = 0;
2452 if (row >= rows) row = rows - 1;
2453 if (col < 0) col = 0;
2454 if (col >= columns) col = columns - 1;
2455
2456 //----- 1. Try the standard terminal ESC sequence
2457 sprintf((char *) cm1, CMrc, row + 1, col + 1);
2458 cm= cm1;
2459 if (! opti) goto pc0;
2460
2461#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2462 //----- find the minimum # of chars to move cursor -------------
2463 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2464 memset(cm2, '\0', BUFSIZ - 1); // clear the buffer
2465
2466 // move to the correct row
2467 while (row < Rrow) {
2468 // the cursor has to move up
2469 strcat(cm2, CMup);
2470 Rrow--;
2471 }
2472 while (row > Rrow) {
2473 // the cursor has to move down
2474 strcat(cm2, CMdown);
2475 Rrow++;
2476 }
2477
2478 // now move to the correct column
2479 strcat(cm2, "\r"); // start at col 0
2480 // just send out orignal source char to get to correct place
2481 screenp = &screen[row * columns]; // start of screen line
2482 strncat(cm2, (char* )screenp, col);
2483
2484 //----- 3. Try some other way of moving cursor
2485 //---------------------------------------------
2486
2487 // pick the shortest cursor motion to send out
2488 cm= cm1;
2489 if (strlen(cm2) < strlen(cm)) {
2490 cm= cm2;
2491 } /* else if (strlen(cm3) < strlen(cm)) {
2492 cm= cm3;
2493 } */
2494#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2495 pc0:
2496 write1(cm); // move the cursor
2497}
2498
2499//----- Erase from cursor to end of line -----------------------
2500static void clear_to_eol(void)
2501{
2502 write1(Ceol); // Erase from cursor to end of line
2503}
2504
2505//----- Erase from cursor to end of screen -----------------------
2506static void clear_to_eos(void)
2507{
2508 write1(Ceos); // Erase from cursor to end of screen
2509}
2510
2511//----- Start standout mode ------------------------------------
2512static void standout_start(void) // send "start reverse video" sequence
2513{
2514 write1(SOs); // Start reverse video mode
2515}
2516
2517//----- End standout mode --------------------------------------
2518static void standout_end(void) // send "end reverse video" sequence
2519{
2520 write1(SOn); // End reverse video mode
2521}
2522
2523//----- Flash the screen --------------------------------------
2524static void flash(int h)
2525{
2526 standout_start(); // send "start reverse video" sequence
2527 redraw(TRUE);
2528 (void) mysleep(h);
2529 standout_end(); // send "end reverse video" sequence
2530 redraw(TRUE);
2531}
2532
2533static void Indicate_Error(void)
2534{
2535#ifdef CONFIG_FEATURE_VI_CRASHME
2536 if (crashme > 0)
2537 return; // generate a random command
2538#endif /* CONFIG_FEATURE_VI_CRASHME */
2539 if (!err_method) {
2540 write1(bell); // send out a bell character
2541 } else {
2542 flash(10);
2543 }
2544}
2545
2546//----- Screen[] Routines --------------------------------------
2547//----- Erase the Screen[] memory ------------------------------
2548static void screen_erase(void)
2549{
2550 memset(screen, ' ', screensize); // clear new screen
2551}
2552
2553static int bufsum(unsigned char *buf, int count)
2554{
2555 int sum = 0;
2556 unsigned char *e = buf + count;
2557 while (buf < e)
2558 sum += *buf++;
2559 return sum;
2560}
2561
2562//----- Draw the status line at bottom of the screen -------------
2563static void show_status_line(void)
2564{
2565 int cnt = 0, cksum = 0;
2566
2567 // either we already have an error or status message, or we
2568 // create one.
2569 if (!have_status_msg) {
2570 cnt = format_edit_status();
2571 cksum = bufsum(status_buffer, cnt);
2572 }
2573 if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
2574 last_status_cksum= cksum; // remember if we have seen this line
2575 place_cursor(rows - 1, 0, FALSE); // put cursor on status line
2576 write1((char*)status_buffer);
2577 clear_to_eol();
2578 if (have_status_msg) {
2579 if (((int)strlen((char*)status_buffer) - (have_status_msg - 1)) >
2580 (columns - 1) ) {
2581 have_status_msg = 0;
2582 Hit_Return();
2583 }
2584 have_status_msg = 0;
2585 }
2586 place_cursor(crow, ccol, FALSE); // put cursor back in correct place
2587 }
2588 fflush(stdout);
2589}
2590
2591//----- format the status buffer, the bottom line of screen ------
2592// format status buffer, with STANDOUT mode
2593static void psbs(const char *format, ...)
2594{
2595 va_list args;
2596
2597 va_start(args, format);
2598 strcpy((char *) status_buffer, SOs); // Terminal standout mode on
2599 vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args);
2600 strcat((char *) status_buffer, SOn); // Terminal standout mode off
2601 va_end(args);
2602
2603 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
2604
2605 return;
2606}
2607
2608// format status buffer
2609static void psb(const char *format, ...)
2610{
2611 va_list args;
2612
2613 va_start(args, format);
2614 vsprintf((char *) status_buffer, format, args);
2615 va_end(args);
2616
2617 have_status_msg = 1;
2618
2619 return;
2620}
2621
2622static void ni(Byte * s) // display messages
2623{
2624 Byte buf[BUFSIZ];
2625
2626 print_literal(buf, s);
2627 psbs("\'%s\' is not implemented", buf);
2628}
2629
2630static int format_edit_status(void) // show file status on status line
2631{
2632 int cur, percent, ret, trunc_at;
2633 static int tot;
2634
2635 // file_modified is now a counter rather than a flag. this
2636 // helps reduce the amount of line counting we need to do.
2637 // (this will cause a mis-reporting of modified status
2638 // once every MAXINT editing operations.)
2639
2640 // it would be nice to do a similar optimization here -- if
2641 // we haven't done a motion that could have changed which line
2642 // we're on, then we shouldn't have to do this count_lines()
2643 cur = count_lines(text, dot);
2644
2645 // reduce counting -- the total lines can't have
2646 // changed if we haven't done any edits.
2647 if (file_modified != last_file_modified) {
2648 tot = cur + count_lines(dot, end - 1) - 1;
2649 last_file_modified = file_modified;
2650 }
2651
2652 // current line percent
2653 // ------------- ~~ ----------
2654 // total lines 100
2655 if (tot > 0) {
2656 percent = (100 * cur) / tot;
2657 } else {
2658 cur = tot = 0;
2659 percent = 100;
2660 }
2661
2662 trunc_at = columns < STATUS_BUFFER_LEN-1 ?
2663 columns : STATUS_BUFFER_LEN-1;
2664
2665 ret = snprintf((char *) status_buffer, trunc_at+1,
2666#ifdef CONFIG_FEATURE_VI_READONLY
2667 "%c %s%s%s %d/%d %d%%",
2668#else
2669 "%c %s%s %d/%d %d%%",
2670#endif
2671 (cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
2672 (cfn != 0 ? (char *) cfn : "No file"),
2673#ifdef CONFIG_FEATURE_VI_READONLY
2674 ((vi_readonly || readonly) ? " [Read-only]" : ""),
2675#endif
2676 (file_modified ? " [modified]" : ""),
2677 cur, tot, percent);
2678
2679 if (ret >= 0 && ret < trunc_at)
2680 return ret; /* it all fit */
2681
2682 return trunc_at; /* had to truncate */
2683}
2684
2685//----- Force refresh of all Lines -----------------------------
2686static void redraw(int full_screen)
2687{
2688 place_cursor(0, 0, FALSE); // put cursor in correct place
2689 clear_to_eos(); // tel terminal to erase display
2690 screen_erase(); // erase the internal screen buffer
2691 last_status_cksum = 0; // force status update
2692 refresh(full_screen); // this will redraw the entire display
2693 show_status_line();
2694}
2695
2696//----- Format a text[] line into a buffer ---------------------
2697static void format_line(Byte *dest, Byte *src, int li)
2698{
2699 int co;
2700 Byte c;
2701
2702 for (co= 0; co < MAX_SCR_COLS; co++) {
2703 c= ' '; // assume blank
2704 if (li > 0 && co == 0) {
2705 c = '~'; // not first line, assume Tilde
2706 }
2707 // are there chars in text[] and have we gone past the end
2708 if (text < end && src < end) {
2709 c = *src++;
2710 }
2711 if (c == '\n')
2712 break;
2713 if (c > 127 && !Isprint(c)) {
2714 c = '.';
2715 }
2716 if (c < ' ' || c == 127) {
2717 if (c == '\t') {
2718 c = ' ';
2719 // co % 8 != 7
2720 for (; (co % tabstop) != (tabstop - 1); co++) {
2721 dest[co] = c;
2722 }
2723 } else {
2724 dest[co++] = '^';
2725 if(c == 127)
2726 c = '?';
2727 else
2728 c += '@'; // make it visible
2729 }
2730 }
2731 // the co++ is done here so that the column will
2732 // not be overwritten when we blank-out the rest of line
2733 dest[co] = c;
2734 if (src >= end)
2735 break;
2736 }
2737}
2738
2739//----- Refresh the changed screen lines -----------------------
2740// Copy the source line from text[] into the buffer and note
2741// if the current screenline is different from the new buffer.
2742// If they differ then that line needs redrawing on the terminal.
2743//
2744static void refresh(int full_screen)
2745{
2746 static int old_offset;
2747 int li, changed;
2748 Byte buf[MAX_SCR_COLS];
2749 Byte *tp, *sp; // pointer into text[] and screen[]
2750#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2751 int last_li= -2; // last line that changed- for optimizing cursor movement
2752#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2753
2754 if (ENABLE_FEATURE_VI_WIN_RESIZE)
2755 get_terminal_width_height(0, &columns, &rows);
2756 sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
2757 tp = screenbegin; // index into text[] of top line
2758
2759 // compare text[] to screen[] and mark screen[] lines that need updating
2760 for (li = 0; li < rows - 1; li++) {
2761 int cs, ce; // column start & end
2762 memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer
2763 buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer
2764 // format current text line into buf
2765 format_line(buf, tp, li);
2766
2767 // skip to the end of the current text[] line
2768 while (tp < end && *tp++ != '\n') /*no-op*/ ;
2769
2770 // see if there are any changes between vitual screen and buf
2771 changed = FALSE; // assume no change
2772 cs= 0;
2773 ce= columns-1;
2774 sp = &screen[li * columns]; // start of screen line
2775 if (full_screen) {
2776 // force re-draw of every single column from 0 - columns-1
2777 goto re0;
2778 }
2779 // compare newly formatted buffer with virtual screen
2780 // look forward for first difference between buf and screen
2781 for ( ; cs <= ce; cs++) {
2782 if (buf[cs + offset] != sp[cs]) {
2783 changed = TRUE; // mark for redraw
2784 break;
2785 }
2786 }
2787
2788 // look backward for last difference between buf and screen
2789 for ( ; ce >= cs; ce--) {
2790 if (buf[ce + offset] != sp[ce]) {
2791 changed = TRUE; // mark for redraw
2792 break;
2793 }
2794 }
2795 // now, cs is index of first diff, and ce is index of last diff
2796
2797 // if horz offset has changed, force a redraw
2798 if (offset != old_offset) {
2799 re0:
2800 changed = TRUE;
2801 }
2802
2803 // make a sanity check of columns indexes
2804 if (cs < 0) cs= 0;
2805 if (ce > columns-1) ce= columns-1;
2806 if (cs > ce) { cs= 0; ce= columns-1; }
2807 // is there a change between vitual screen and buf
2808 if (changed) {
2809 // copy changed part of buffer to virtual screen
2810 memmove(sp+cs, buf+(cs+offset), ce-cs+1);
2811
2812 // move cursor to column of first change
2813 if (offset != old_offset) {
2814 // opti_cur_move is still too stupid
2815 // to handle offsets correctly
2816 place_cursor(li, cs, FALSE);
2817 } else {
2818#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2819 // if this just the next line
2820 // try to optimize cursor movement
2821 // otherwise, use standard ESC sequence
2822 place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE);
2823 last_li= li;
2824#else /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2825 place_cursor(li, cs, FALSE); // use standard ESC sequence
2826#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2827 }
2828
2829 // write line out to terminal
2830 {
2831 int nic = ce-cs+1;
2832 char *out = (char*)sp+cs;
2833
2834 while(nic-- > 0) {
2835 putchar(*out);
2836 out++;
2837 }
2838 }
2839#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2840 last_row = li;
2841#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2842 }
2843 }
2844
2845#ifdef CONFIG_FEATURE_VI_OPTIMIZE_CURSOR
2846 place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE);
2847 last_row = crow;
2848#else
2849 place_cursor(crow, ccol, FALSE);
2850#endif /* CONFIG_FEATURE_VI_OPTIMIZE_CURSOR */
2851
2852 if (offset != old_offset)
2853 old_offset = offset;
2854}
2855
2856//---------------------------------------------------------------------
2857//----- the Ascii Chart -----------------------------------------------
2858//
2859// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
2860// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
2861// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
2862// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
2863// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
2864// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
2865// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
2866// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
2867// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
2868// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
2869// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
2870// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
2871// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
2872// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
2873// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
2874// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
2875//---------------------------------------------------------------------
2876
2877//----- Execute a Vi Command -----------------------------------
2878static void do_cmd(Byte c)
2879{
2880 Byte c1, *p, *q, *msg, buf[9], *save_dot;
2881 int cnt, i, j, dir, yf;
2882
2883 c1 = c; // quiet the compiler
2884 cnt = yf = dir = 0; // quiet the compiler
2885 p = q = save_dot = msg = buf; // quiet the compiler
2886 memset(buf, '\0', 9); // clear buf
2887
2888 show_status_line();
2889
2890 /* if this is a cursor key, skip these checks */
2891 switch (c) {
2892 case VI_K_UP:
2893 case VI_K_DOWN:
2894 case VI_K_LEFT:
2895 case VI_K_RIGHT:
2896 case VI_K_HOME:
2897 case VI_K_END:
2898 case VI_K_PAGEUP:
2899 case VI_K_PAGEDOWN:
2900 goto key_cmd_mode;
2901 }
2902
2903 if (cmd_mode == 2) {
2904 // flip-flop Insert/Replace mode
2905 if (c == VI_K_INSERT) goto dc_i;
2906 // we are 'R'eplacing the current *dot with new char
2907 if (*dot == '\n') {
2908 // don't Replace past E-o-l
2909 cmd_mode = 1; // convert to insert
2910 } else {
2911 if (1 <= c || Isprint(c)) {
2912 if (c != 27)
2913 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
2914 dot = char_insert(dot, c); // insert new char
2915 }
2916 goto dc1;
2917 }
2918 }
2919 if (cmd_mode == 1) {
2920 // hitting "Insert" twice means "R" replace mode
2921 if (c == VI_K_INSERT) goto dc5;
2922 // insert the char c at "dot"
2923 if (1 <= c || Isprint(c)) {
2924 dot = char_insert(dot, c);
2925 }
2926 goto dc1;
2927 }
2928
2929key_cmd_mode:
2930 switch (c) {
2931 //case 0x01: // soh
2932 //case 0x09: // ht
2933 //case 0x0b: // vt
2934 //case 0x0e: // so
2935 //case 0x0f: // si
2936 //case 0x10: // dle
2937 //case 0x11: // dc1
2938 //case 0x13: // dc3
2939#ifdef CONFIG_FEATURE_VI_CRASHME
2940 case 0x14: // dc4 ctrl-T
2941 crashme = (crashme == 0) ? 1 : 0;
2942 break;
2943#endif /* CONFIG_FEATURE_VI_CRASHME */
2944 //case 0x16: // syn
2945 //case 0x17: // etb
2946 //case 0x18: // can
2947 //case 0x1c: // fs
2948 //case 0x1d: // gs
2949 //case 0x1e: // rs
2950 //case 0x1f: // us
2951 //case '!': // !-
2952 //case '#': // #-
2953 //case '&': // &-
2954 //case '(': // (-
2955 //case ')': // )-
2956 //case '*': // *-
2957 //case ',': // ,-
2958 //case '=': // =-
2959 //case '@': // @-
2960 //case 'F': // F-
2961 //case 'K': // K-
2962 //case 'Q': // Q-
2963 //case 'S': // S-
2964 //case 'T': // T-
2965 //case 'V': // V-
2966 //case '[': // [-
2967 //case '\\': // \-
2968 //case ']': // ]-
2969 //case '_': // _-
2970 //case '`': // `-
2971 //case 'g': // g-
2972 //case 'u': // u- FIXME- there is no undo
2973 //case 'v': // v-
2974 default: // unrecognised command
2975 buf[0] = c;
2976 buf[1] = '\0';
2977 if (c < ' ') {
2978 buf[0] = '^';
2979 buf[1] = c + '@';
2980 buf[2] = '\0';
2981 }
2982 ni((Byte *) buf);
2983 end_cmd_q(); // stop adding to q
2984 case 0x00: // nul- ignore
2985 break;
2986 case 2: // ctrl-B scroll up full screen
2987 case VI_K_PAGEUP: // Cursor Key Page Up
2988 dot_scroll(rows - 2, -1);
2989 break;
2990#ifdef CONFIG_FEATURE_VI_USE_SIGNALS
2991 case 0x03: // ctrl-C interrupt
2992 longjmp(restart, 1);
2993 break;
2994 case 26: // ctrl-Z suspend
2995 suspend_sig(SIGTSTP);
2996 break;
2997#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */
2998 case 4: // ctrl-D scroll down half screen
2999 dot_scroll((rows - 2) / 2, 1);
3000 break;
3001 case 5: // ctrl-E scroll down one line
3002 dot_scroll(1, 1);
3003 break;
3004 case 6: // ctrl-F scroll down full screen
3005 case VI_K_PAGEDOWN: // Cursor Key Page Down
3006 dot_scroll(rows - 2, 1);
3007 break;
3008 case 7: // ctrl-G show current status
3009 last_status_cksum = 0; // force status update
3010 break;
3011 case 'h': // h- move left
3012 case VI_K_LEFT: // cursor key Left
3013 case 8: // ctrl-H- move left (This may be ERASE char)
3014 case 127: // DEL- move left (This may be ERASE char)
3015 if (cmdcnt-- > 1) {
3016 do_cmd(c);
3017 } // repeat cnt
3018 dot_left();
3019 break;
3020 case 10: // Newline ^J
3021 case 'j': // j- goto next line, same col
3022 case VI_K_DOWN: // cursor key Down
3023 if (cmdcnt-- > 1) {
3024 do_cmd(c);
3025 } // repeat cnt
3026 dot_next(); // go to next B-o-l
3027 dot = move_to_col(dot, ccol + offset); // try stay in same col
3028 break;
3029 case 12: // ctrl-L force redraw whole screen
3030 case 18: // ctrl-R force redraw
3031 place_cursor(0, 0, FALSE); // put cursor in correct place
3032 clear_to_eos(); // tel terminal to erase display
3033 (void) mysleep(10);
3034 screen_erase(); // erase the internal screen buffer
3035 last_status_cksum = 0; // force status update
3036 refresh(TRUE); // this will redraw the entire display
3037 break;
3038 case 13: // Carriage Return ^M
3039 case '+': // +- goto next line
3040 if (cmdcnt-- > 1) {
3041 do_cmd(c);
3042 } // repeat cnt
3043 dot_next();
3044 dot_skip_over_ws();
3045 break;
3046 case 21: // ctrl-U scroll up half screen
3047 dot_scroll((rows - 2) / 2, -1);
3048 break;
3049 case 25: // ctrl-Y scroll up one line
3050 dot_scroll(1, -1);
3051 break;
3052 case 27: // esc
3053 if (cmd_mode == 0)
3054 indicate_error(c);
3055 cmd_mode = 0; // stop insrting
3056 end_cmd_q();
3057 last_status_cksum = 0; // force status update
3058 break;
3059 case ' ': // move right
3060 case 'l': // move right
3061 case VI_K_RIGHT: // Cursor Key Right
3062 if (cmdcnt-- > 1) {
3063 do_cmd(c);
3064 } // repeat cnt
3065 dot_right();
3066 break;
3067#ifdef CONFIG_FEATURE_VI_YANKMARK
3068 case '"': // "- name a register to use for Delete/Yank
3069 c1 = get_one_char();
3070 c1 = tolower(c1);
3071 if (islower(c1)) {
3072 YDreg = c1 - 'a';
3073 } else {
3074 indicate_error(c);
3075 }
3076 break;
3077 case '\'': // '- goto a specific mark
3078 c1 = get_one_char();
3079 c1 = tolower(c1);
3080 if (islower(c1)) {
3081 c1 = c1 - 'a';
3082 // get the b-o-l
3083 q = mark[(int) c1];
3084 if (text <= q && q < end) {
3085 dot = q;
3086 dot_begin(); // go to B-o-l
3087 dot_skip_over_ws();
3088 }
3089 } else if (c1 == '\'') { // goto previous context
3090 dot = swap_context(dot); // swap current and previous context
3091 dot_begin(); // go to B-o-l
3092 dot_skip_over_ws();
3093 } else {
3094 indicate_error(c);
3095 }
3096 break;
3097 case 'm': // m- Mark a line
3098 // this is really stupid. If there are any inserts or deletes
3099 // between text[0] and dot then this mark will not point to the
3100 // correct location! It could be off by many lines!
3101 // Well..., at least its quick and dirty.
3102 c1 = get_one_char();
3103 c1 = tolower(c1);
3104 if (islower(c1)) {
3105 c1 = c1 - 'a';
3106 // remember the line
3107 mark[(int) c1] = dot;
3108 } else {
3109 indicate_error(c);
3110 }
3111 break;
3112 case 'P': // P- Put register before
3113 case 'p': // p- put register after
3114 p = reg[YDreg];
3115 if (p == 0) {
3116 psbs("Nothing in register %c", what_reg());
3117 break;
3118 }
3119 // are we putting whole lines or strings
3120 if (strchr((char *) p, '\n') != NULL) {
3121 if (c == 'P') {
3122 dot_begin(); // putting lines- Put above
3123 }
3124 if (c == 'p') {
3125 // are we putting after very last line?
3126 if (end_line(dot) == (end - 1)) {
3127 dot = end; // force dot to end of text[]
3128 } else {
3129 dot_next(); // next line, then put before
3130 }
3131 }
3132 } else {
3133 if (c == 'p')
3134 dot_right(); // move to right, can move to NL
3135 }
3136 dot = string_insert(dot, p); // insert the string
3137 end_cmd_q(); // stop adding to q
3138 break;
3139 case 'U': // U- Undo; replace current line with original version
3140 if (reg[Ureg] != 0) {
3141 p = begin_line(dot);
3142 q = end_line(dot);
3143 p = text_hole_delete(p, q); // delete cur line
3144 p = string_insert(p, reg[Ureg]); // insert orig line
3145 dot = p;
3146 dot_skip_over_ws();
3147 }
3148 break;
3149#endif /* CONFIG_FEATURE_VI_YANKMARK */
3150 case '$': // $- goto end of line
3151 case VI_K_END: // Cursor Key End
3152 if (cmdcnt-- > 1) {
3153 do_cmd(c);
3154 } // repeat cnt
3155 dot = end_line(dot);
3156 break;
3157 case '%': // %- find matching char of pair () [] {}
3158 for (q = dot; q < end && *q != '\n'; q++) {
3159 if (strchr("()[]{}", *q) != NULL) {
3160 // we found half of a pair
3161 p = find_pair(q, *q);
3162 if (p == NULL) {
3163 indicate_error(c);
3164 } else {
3165 dot = p;
3166 }
3167 break;
3168 }
3169 }
3170 if (*q == '\n')
3171 indicate_error(c);
3172 break;
3173 case 'f': // f- forward to a user specified char
3174 last_forward_char = get_one_char(); // get the search char
3175 //
3176 // dont separate these two commands. 'f' depends on ';'
3177 //
3178 //**** fall thru to ... ';'
3179 case ';': // ;- look at rest of line for last forward char
3180 if (cmdcnt-- > 1) {
3181 do_cmd(';');
3182 } // repeat cnt
3183 if (last_forward_char == 0) break;
3184 q = dot + 1;
3185 while (q < end - 1 && *q != '\n' && *q != last_forward_char) {
3186 q++;
3187 }
3188 if (*q == last_forward_char)
3189 dot = q;
3190 break;
3191 case '-': // -- goto prev line
3192 if (cmdcnt-- > 1) {
3193 do_cmd(c);
3194 } // repeat cnt
3195 dot_prev();
3196 dot_skip_over_ws();
3197 break;
3198#ifdef CONFIG_FEATURE_VI_DOT_CMD
3199 case '.': // .- repeat the last modifying command
3200 // Stuff the last_modifying_cmd back into stdin
3201 // and let it be re-executed.
3202 if (last_modifying_cmd != 0) {
3203 ioq = ioq_start = (Byte *) bb_xstrdup((char *) last_modifying_cmd);
3204 }
3205 break;
3206#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3207#ifdef CONFIG_FEATURE_VI_SEARCH
3208 case '?': // /- search for a pattern
3209 case '/': // /- search for a pattern
3210 buf[0] = c;
3211 buf[1] = '\0';
3212 q = get_input_line(buf); // get input line- use "status line"
3213 if (strlen((char *) q) == 1)
3214 goto dc3; // if no pat re-use old pat
3215 if (strlen((char *) q) > 1) { // new pat- save it and find
3216 // there is a new pat
3217 free(last_search_pattern);
3218 last_search_pattern = (Byte *) bb_xstrdup((char *) q);
3219 goto dc3; // now find the pattern
3220 }
3221 // user changed mind and erased the "/"- do nothing
3222 break;
3223 case 'N': // N- backward search for last pattern
3224 if (cmdcnt-- > 1) {
3225 do_cmd(c);
3226 } // repeat cnt
3227 dir = BACK; // assume BACKWARD search
3228 p = dot - 1;
3229 if (last_search_pattern[0] == '?') {
3230 dir = FORWARD;
3231 p = dot + 1;
3232 }
3233 goto dc4; // now search for pattern
3234 break;
3235 case 'n': // n- repeat search for last pattern
3236 // search rest of text[] starting at next char
3237 // if search fails return orignal "p" not the "p+1" address
3238 if (cmdcnt-- > 1) {
3239 do_cmd(c);
3240 } // repeat cnt
3241 dc3:
3242 if (last_search_pattern == 0) {
3243 msg = (Byte *) "No previous regular expression";
3244 goto dc2;
3245 }
3246 if (last_search_pattern[0] == '/') {
3247 dir = FORWARD; // assume FORWARD search
3248 p = dot + 1;
3249 }
3250 if (last_search_pattern[0] == '?') {
3251 dir = BACK;
3252 p = dot - 1;
3253 }
3254 dc4:
3255 q = char_search(p, last_search_pattern + 1, dir, FULL);
3256 if (q != NULL) {
3257 dot = q; // good search, update "dot"
3258 msg = (Byte *) "";
3259 goto dc2;
3260 }
3261 // no pattern found between "dot" and "end"- continue at top
3262 p = text;
3263 if (dir == BACK) {
3264 p = end - 1;
3265 }
3266 q = char_search(p, last_search_pattern + 1, dir, FULL);
3267 if (q != NULL) { // found something
3268 dot = q; // found new pattern- goto it
3269 msg = (Byte *) "search hit BOTTOM, continuing at TOP";
3270 if (dir == BACK) {
3271 msg = (Byte *) "search hit TOP, continuing at BOTTOM";
3272 }
3273 } else {
3274 msg = (Byte *) "Pattern not found";
3275 }
3276 dc2:
3277 if (*msg) psbs("%s", msg);
3278 break;
3279 case '{': // {- move backward paragraph
3280 q = char_search(dot, (Byte *) "\n\n", BACK, FULL);
3281 if (q != NULL) { // found blank line
3282 dot = next_line(q); // move to next blank line
3283 }
3284 break;
3285 case '}': // }- move forward paragraph
3286 q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL);
3287 if (q != NULL) { // found blank line
3288 dot = next_line(q); // move to next blank line
3289 }
3290 break;
3291#endif /* CONFIG_FEATURE_VI_SEARCH */
3292 case '0': // 0- goto begining of line
3293 case '1': // 1-
3294 case '2': // 2-
3295 case '3': // 3-
3296 case '4': // 4-
3297 case '5': // 5-
3298 case '6': // 6-
3299 case '7': // 7-
3300 case '8': // 8-
3301 case '9': // 9-
3302 if (c == '0' && cmdcnt < 1) {
3303 dot_begin(); // this was a standalone zero
3304 } else {
3305 cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
3306 }
3307 break;
3308 case ':': // :- the colon mode commands
3309 p = get_input_line((Byte *) ":"); // get input line- use "status line"
3310#ifdef CONFIG_FEATURE_VI_COLON
3311 colon(p); // execute the command
3312#else /* CONFIG_FEATURE_VI_COLON */
3313 if (*p == ':')
3314 p++; // move past the ':'
3315 cnt = strlen((char *) p);
3316 if (cnt <= 0)
3317 break;
3318 if (strncasecmp((char *) p, "quit", cnt) == 0 ||
3319 strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines
3320 if (file_modified && p[1] != '!') {
3321 psbs("No write since last change (:quit! overrides)");
3322 } else {
3323 editing = 0;
3324 }
3325 } else if (strncasecmp((char *) p, "write", cnt) == 0
3326 || strncasecmp((char *) p, "wq", cnt) == 0
3327 || strncasecmp((char *) p, "wn", cnt) == 0
3328 || strncasecmp((char *) p, "x", cnt) == 0) {
3329 cnt = file_write(cfn, text, end - 1);
3330 if (cnt < 0) {
3331 if (cnt == -1)
3332 psbs("Write error: %s", strerror(errno));
3333 } else {
3334 file_modified = 0;
3335 last_file_modified = -1;
3336 psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
3337 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' ||
3338 p[0] == 'X' || p[1] == 'Q' || p[1] == 'N') {
3339 editing = 0;
3340 }
3341 }
3342 } else if (strncasecmp((char *) p, "file", cnt) == 0 ) {
3343 last_status_cksum = 0; // force status update
3344 } else if (sscanf((char *) p, "%d", &j) > 0) {
3345 dot = find_line(j); // go to line # j
3346 dot_skip_over_ws();
3347 } else { // unrecognised cmd
3348 ni((Byte *) p);
3349 }
3350#endif /* CONFIG_FEATURE_VI_COLON */
3351 break;
3352 case '<': // <- Left shift something
3353 case '>': // >- Right shift something
3354 cnt = count_lines(text, dot); // remember what line we are on
3355 c1 = get_one_char(); // get the type of thing to delete
3356 find_range(&p, &q, c1);
3357 (void) yank_delete(p, q, 1, YANKONLY); // save copy before change
3358 p = begin_line(p);
3359 q = end_line(q);
3360 i = count_lines(p, q); // # of lines we are shifting
3361 for ( ; i > 0; i--, p = next_line(p)) {
3362 if (c == '<') {
3363 // shift left- remove tab or 8 spaces
3364 if (*p == '\t') {
3365 // shrink buffer 1 char
3366 (void) text_hole_delete(p, p);
3367 } else if (*p == ' ') {
3368 // we should be calculating columns, not just SPACE
3369 for (j = 0; *p == ' ' && j < tabstop; j++) {
3370 (void) text_hole_delete(p, p);
3371 }
3372 }
3373 } else if (c == '>') {
3374 // shift right -- add tab or 8 spaces
3375 (void) char_insert(p, '\t');
3376 }
3377 }
3378 dot = find_line(cnt); // what line were we on
3379 dot_skip_over_ws();
3380 end_cmd_q(); // stop adding to q
3381 break;
3382 case 'A': // A- append at e-o-l
3383 dot_end(); // go to e-o-l
3384 //**** fall thru to ... 'a'
3385 case 'a': // a- append after current char
3386 if (*dot != '\n')
3387 dot++;
3388 goto dc_i;
3389 break;
3390 case 'B': // B- back a blank-delimited Word
3391 case 'E': // E- end of a blank-delimited word
3392 case 'W': // W- forward a blank-delimited word
3393 if (cmdcnt-- > 1) {
3394 do_cmd(c);
3395 } // repeat cnt
3396 dir = FORWARD;
3397 if (c == 'B')
3398 dir = BACK;
3399 if (c == 'W' || isspace(dot[dir])) {
3400 dot = skip_thing(dot, 1, dir, S_TO_WS);
3401 dot = skip_thing(dot, 2, dir, S_OVER_WS);
3402 }
3403 if (c != 'W')
3404 dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
3405 break;
3406 case 'C': // C- Change to e-o-l
3407 case 'D': // D- delete to e-o-l
3408 save_dot = dot;
3409 dot = dollar_line(dot); // move to before NL
3410 // copy text into a register and delete
3411 dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l
3412 if (c == 'C')
3413 goto dc_i; // start inserting
3414#ifdef CONFIG_FEATURE_VI_DOT_CMD
3415 if (c == 'D')
3416 end_cmd_q(); // stop adding to q
3417#endif /* CONFIG_FEATURE_VI_DOT_CMD */
3418 break;
3419 case 'G': // G- goto to a line number (default= E-O-F)
3420 dot = end - 1; // assume E-O-F
3421 if (cmdcnt > 0) {
3422 dot = find_line(cmdcnt); // what line is #cmdcnt
3423 }
3424 dot_skip_over_ws();
3425 break;
3426 case 'H': // H- goto top line on screen
3427 dot = screenbegin;
3428 if (cmdcnt > (rows - 1)) {
3429 cmdcnt = (rows - 1);
3430 }
3431 if (cmdcnt-- > 1) {
3432 do_cmd('+');
3433 } // repeat cnt
3434 dot_skip_over_ws();
3435 break;
3436 case 'I': // I- insert before first non-blank
3437 dot_begin(); // 0
3438 dot_skip_over_ws();
3439 //**** fall thru to ... 'i'
3440 case 'i': // i- insert before current char
3441 case VI_K_INSERT: // Cursor Key Insert
3442 dc_i:
3443 cmd_mode = 1; // start insrting
3444 break;
3445 case 'J': // J- join current and next lines together
3446 if (cmdcnt-- > 2) {
3447 do_cmd(c);
3448 } // repeat cnt
3449 dot_end(); // move to NL
3450 if (dot < end - 1) { // make sure not last char in text[]
3451 *dot++ = ' '; // replace NL with space
3452 file_modified++;
3453 while (isblnk(*dot)) { // delete leading WS
3454 dot_delete();
3455 }
3456 }
3457 end_cmd_q(); // stop adding to q
3458 break;
3459 case 'L': // L- goto bottom line on screen
3460 dot = end_screen();
3461 if (cmdcnt > (rows - 1)) {
3462 cmdcnt = (rows - 1);
3463 }
3464 if (cmdcnt-- > 1) {
3465 do_cmd('-');
3466 } // repeat cnt
3467 dot_begin();
3468 dot_skip_over_ws();
3469 break;
3470 case 'M': // M- goto middle line on screen
3471 dot = screenbegin;
3472 for (cnt = 0; cnt < (rows-1) / 2; cnt++)
3473 dot = next_line(dot);
3474 break;
3475 case 'O': // O- open a empty line above
3476 // 0i\n ESC -i
3477 p = begin_line(dot);
3478 if (p[-1] == '\n') {
3479 dot_prev();
3480 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..."
3481 dot_end();
3482 dot = char_insert(dot, '\n');
3483 } else {
3484 dot_begin(); // 0
3485 dot = char_insert(dot, '\n'); // i\n ESC
3486 dot_prev(); // -
3487 }
3488 goto dc_i;
3489 break;
3490 case 'R': // R- continuous Replace char
3491 dc5:
3492 cmd_mode = 2;
3493 break;
3494 case 'X': // X- delete char before dot
3495 case 'x': // x- delete the current char
3496 case 's': // s- substitute the current char
3497 if (cmdcnt-- > 1) {
3498 do_cmd(c);
3499 } // repeat cnt
3500 dir = 0;
3501 if (c == 'X')
3502 dir = -1;
3503 if (dot[dir] != '\n') {
3504 if (c == 'X')
3505 dot--; // delete prev char
3506 dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
3507 }
3508 if (c == 's')
3509 goto dc_i; // start insrting
3510 end_cmd_q(); // stop adding to q
3511 break;
3512 case 'Z': // Z- if modified, {write}; exit
3513 // ZZ means to save file (if necessary), then exit
3514 c1 = get_one_char();
3515 if (c1 != 'Z') {
3516 indicate_error(c);
3517 break;
3518 }
3519 if (file_modified) {
3520#ifdef CONFIG_FEATURE_VI_READONLY
3521 if (vi_readonly || readonly) {
3522 psbs("\"%s\" File is read only", cfn);
3523 break;
3524 }
3525#endif /* CONFIG_FEATURE_VI_READONLY */
3526 cnt = file_write(cfn, text, end - 1);
3527 if (cnt < 0) {
3528 if (cnt == -1)
3529 psbs("Write error: %s", strerror(errno));
3530 } else if (cnt == (end - 1 - text + 1)) {
3531 editing = 0;
3532 }
3533 } else {
3534 editing = 0;
3535 }
3536 break;
3537 case '^': // ^- move to first non-blank on line
3538 dot_begin();
3539 dot_skip_over_ws();
3540 break;
3541 case 'b': // b- back a word
3542 case 'e': // e- end of word
3543 if (cmdcnt-- > 1) {
3544 do_cmd(c);
3545 } // repeat cnt
3546 dir = FORWARD;
3547 if (c == 'b')
3548 dir = BACK;
3549 if ((dot + dir) < text || (dot + dir) > end - 1)
3550 break;
3551 dot += dir;
3552 if (isspace(*dot)) {
3553 dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
3554 }
3555 if (isalnum(*dot) || *dot == '_') {
3556 dot = skip_thing(dot, 1, dir, S_END_ALNUM);
3557 } else if (ispunct(*dot)) {
3558 dot = skip_thing(dot, 1, dir, S_END_PUNCT);
3559 }
3560 break;
3561 case 'c': // c- change something
3562 case 'd': // d- delete something
3563#ifdef CONFIG_FEATURE_VI_YANKMARK
3564 case 'y': // y- yank something
3565 case 'Y': // Y- Yank a line
3566#endif /* CONFIG_FEATURE_VI_YANKMARK */
3567 yf = YANKDEL; // assume either "c" or "d"
3568#ifdef CONFIG_FEATURE_VI_YANKMARK
3569 if (c == 'y' || c == 'Y')
3570 yf = YANKONLY;
3571#endif /* CONFIG_FEATURE_VI_YANKMARK */
3572 c1 = 'y';
3573 if (c != 'Y')
3574 c1 = get_one_char(); // get the type of thing to delete
3575 find_range(&p, &q, c1);
3576 if (c1 == 27) { // ESC- user changed mind and wants out
3577 c = c1 = 27; // Escape- do nothing
3578 } else if (strchr("wW", c1)) {
3579 if (c == 'c') {
3580 // don't include trailing WS as part of word
3581 while (isblnk(*q)) {
3582 if (q <= text || q[-1] == '\n')
3583 break;
3584 q--;
3585 }
3586 }
3587 dot = yank_delete(p, q, 0, yf); // delete word
3588 } else if (strchr("^0bBeEft$", c1)) {
3589 // single line copy text into a register and delete
3590 dot = yank_delete(p, q, 0, yf); // delete word
3591 } else if (strchr("cdykjHL%+-{}\r\n", c1)) {
3592 // multiple line copy text into a register and delete
3593 dot = yank_delete(p, q, 1, yf); // delete lines
3594 if (c == 'c') {
3595 dot = char_insert(dot, '\n');
3596 // on the last line of file don't move to prev line
3597 if (dot != (end-1)) {
3598 dot_prev();
3599 }
3600 } else if (c == 'd') {
3601 dot_begin();
3602 dot_skip_over_ws();
3603 }
3604 } else {
3605 // could not recognize object
3606 c = c1 = 27; // error-
3607 indicate_error(c);
3608 }
3609 if (c1 != 27) {
3610 // if CHANGING, not deleting, start inserting after the delete
3611 if (c == 'c') {
3612 strcpy((char *) buf, "Change");
3613 goto dc_i; // start inserting
3614 }
3615 if (c == 'd') {
3616 strcpy((char *) buf, "Delete");
3617 }
3618#ifdef CONFIG_FEATURE_VI_YANKMARK
3619 if (c == 'y' || c == 'Y') {
3620 strcpy((char *) buf, "Yank");
3621 }
3622 p = reg[YDreg];
3623 q = p + strlen((char *) p);
3624 for (cnt = 0; p <= q; p++) {
3625 if (*p == '\n')
3626 cnt++;
3627 }
3628 psb("%s %d lines (%d chars) using [%c]",
3629 buf, cnt, strlen((char *) reg[YDreg]), what_reg());
3630#endif /* CONFIG_FEATURE_VI_YANKMARK */
3631 end_cmd_q(); // stop adding to q
3632 }
3633 break;
3634 case 'k': // k- goto prev line, same col
3635 case VI_K_UP: // cursor key Up
3636 if (cmdcnt-- > 1) {
3637 do_cmd(c);
3638 } // repeat cnt
3639 dot_prev();
3640 dot = move_to_col(dot, ccol + offset); // try stay in same col
3641 break;
3642 case 'r': // r- replace the current char with user input
3643 c1 = get_one_char(); // get the replacement char
3644 if (*dot != '\n') {
3645 *dot = c1;
3646 file_modified++; // has the file been modified
3647 }
3648 end_cmd_q(); // stop adding to q
3649 break;
3650 case 't': // t- move to char prior to next x
3651 last_forward_char = get_one_char();
3652 do_cmd(';');
3653 if (*dot == last_forward_char)
3654 dot_left();
3655 last_forward_char= 0;
3656 break;
3657 case 'w': // w- forward a word
3658 if (cmdcnt-- > 1) {
3659 do_cmd(c);
3660 } // repeat cnt
3661 if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
3662 dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
3663 } else if (ispunct(*dot)) { // we are on PUNCT
3664 dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
3665 }
3666 if (dot < end - 1)
3667 dot++; // move over word
3668 if (isspace(*dot)) {
3669 dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
3670 }
3671 break;
3672 case 'z': // z-
3673 c1 = get_one_char(); // get the replacement char
3674 cnt = 0;
3675 if (c1 == '.')
3676 cnt = (rows - 2) / 2; // put dot at center
3677 if (c1 == '-')
3678 cnt = rows - 2; // put dot at bottom
3679 screenbegin = begin_line(dot); // start dot at top
3680 dot_scroll(cnt, -1);
3681 break;
3682 case '|': // |- move to column "cmdcnt"
3683 dot = move_to_col(dot, cmdcnt - 1); // try to move to column
3684 break;
3685 case '~': // ~- flip the case of letters a-z -> A-Z
3686 if (cmdcnt-- > 1) {
3687 do_cmd(c);
3688 } // repeat cnt
3689 if (islower(*dot)) {
3690 *dot = toupper(*dot);
3691 file_modified++; // has the file been modified
3692 } else if (isupper(*dot)) {
3693 *dot = tolower(*dot);
3694 file_modified++; // has the file been modified
3695 }
3696 dot_right();
3697 end_cmd_q(); // stop adding to q
3698 break;
3699 //----- The Cursor and Function Keys -----------------------------
3700 case VI_K_HOME: // Cursor Key Home
3701 dot_begin();
3702 break;
3703 // The Fn keys could point to do_macro which could translate them
3704 case VI_K_FUN1: // Function Key F1
3705 case VI_K_FUN2: // Function Key F2
3706 case VI_K_FUN3: // Function Key F3
3707 case VI_K_FUN4: // Function Key F4
3708 case VI_K_FUN5: // Function Key F5
3709 case VI_K_FUN6: // Function Key F6
3710 case VI_K_FUN7: // Function Key F7
3711 case VI_K_FUN8: // Function Key F8
3712 case VI_K_FUN9: // Function Key F9
3713 case VI_K_FUN10: // Function Key F10
3714 case VI_K_FUN11: // Function Key F11
3715 case VI_K_FUN12: // Function Key F12
3716 break;
3717 }
3718
3719 dc1:
3720 // if text[] just became empty, add back an empty line
3721 if (end == text) {
3722 (void) char_insert(text, '\n'); // start empty buf with dummy line
3723 dot = text;
3724 }
3725 // it is OK for dot to exactly equal to end, otherwise check dot validity
3726 if (dot != end) {
3727 dot = bound_dot(dot); // make sure "dot" is valid
3728 }
3729#ifdef CONFIG_FEATURE_VI_YANKMARK
3730 check_context(c); // update the current context
3731#endif /* CONFIG_FEATURE_VI_YANKMARK */
3732
3733 if (!isdigit(c))
3734 cmdcnt = 0; // cmd was not a number, reset cmdcnt
3735 cnt = dot - begin_line(dot);
3736 // Try to stay off of the Newline
3737 if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
3738 dot--;
3739}
3740
3741#ifdef CONFIG_FEATURE_VI_CRASHME
3742static int totalcmds = 0;
3743static int Mp = 85; // Movement command Probability
3744static int Np = 90; // Non-movement command Probability
3745static int Dp = 96; // Delete command Probability
3746static int Ip = 97; // Insert command Probability
3747static int Yp = 98; // Yank command Probability
3748static int Pp = 99; // Put command Probability
3749static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
3750char chars[20] = "\t012345 abcdABCD-=.$";
3751char *words[20] = { "this", "is", "a", "test",
3752 "broadcast", "the", "emergency", "of",
3753 "system", "quick", "brown", "fox",
3754 "jumped", "over", "lazy", "dogs",
3755 "back", "January", "Febuary", "March"
3756};
3757char *lines[20] = {
3758 "You should have received a copy of the GNU General Public License\n",
3759 "char c, cm, *cmd, *cmd1;\n",
3760 "generate a command by percentages\n",
3761 "Numbers may be typed as a prefix to some commands.\n",
3762 "Quit, discarding changes!\n",
3763 "Forced write, if permission originally not valid.\n",
3764 "In general, any ex or ed command (such as substitute or delete).\n",
3765 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3766 "Please get w/ me and I will go over it with you.\n",
3767 "The following is a list of scheduled, committed changes.\n",
3768 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3769 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3770 "Any question about transactions please contact Sterling Huxley.\n",
3771 "I will try to get back to you by Friday, December 31.\n",
3772 "This Change will be implemented on Friday.\n",
3773 "Let me know if you have problems accessing this;\n",
3774 "Sterling Huxley recently added you to the access list.\n",
3775 "Would you like to go to lunch?\n",
3776 "The last command will be automatically run.\n",
3777 "This is too much english for a computer geek.\n",
3778};
3779char *multilines[20] = {
3780 "You should have received a copy of the GNU General Public License\n",
3781 "char c, cm, *cmd, *cmd1;\n",
3782 "generate a command by percentages\n",
3783 "Numbers may be typed as a prefix to some commands.\n",
3784 "Quit, discarding changes!\n",
3785 "Forced write, if permission originally not valid.\n",
3786 "In general, any ex or ed command (such as substitute or delete).\n",
3787 "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
3788 "Please get w/ me and I will go over it with you.\n",
3789 "The following is a list of scheduled, committed changes.\n",
3790 "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
3791 "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
3792 "Any question about transactions please contact Sterling Huxley.\n",
3793 "I will try to get back to you by Friday, December 31.\n",
3794 "This Change will be implemented on Friday.\n",
3795 "Let me know if you have problems accessing this;\n",
3796 "Sterling Huxley recently added you to the access list.\n",
3797 "Would you like to go to lunch?\n",
3798 "The last command will be automatically run.\n",
3799 "This is too much english for a computer geek.\n",
3800};
3801
3802// create a random command to execute
3803static void crash_dummy()
3804{
3805 static int sleeptime; // how long to pause between commands
3806 char c, cm, *cmd, *cmd1;
3807 int i, cnt, thing, rbi, startrbi, percent;
3808
3809 // "dot" movement commands
3810 cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
3811
3812 // is there already a command running?
3813 if (readed_for_parse > 0)
3814 goto cd1;
3815 cd0:
3816 startrbi = rbi = 0;
3817 sleeptime = 0; // how long to pause between commands
3818 memset(readbuffer, '\0', BUFSIZ); // clear the read buffer
3819 // generate a command by percentages
3820 percent = (int) lrand48() % 100; // get a number from 0-99
3821 if (percent < Mp) { // Movement commands
3822 // available commands
3823 cmd = cmd1;
3824 M++;
3825 } else if (percent < Np) { // non-movement commands
3826 cmd = "mz<>\'\""; // available commands
3827 N++;
3828 } else if (percent < Dp) { // Delete commands
3829 cmd = "dx"; // available commands
3830 D++;
3831 } else if (percent < Ip) { // Inset commands
3832 cmd = "iIaAsrJ"; // available commands
3833 I++;
3834 } else if (percent < Yp) { // Yank commands
3835 cmd = "yY"; // available commands
3836 Y++;
3837 } else if (percent < Pp) { // Put commands
3838 cmd = "pP"; // available commands
3839 P++;
3840 } else {
3841 // We do not know how to handle this command, try again
3842 U++;
3843 goto cd0;
3844 }
3845 // randomly pick one of the available cmds from "cmd[]"
3846 i = (int) lrand48() % strlen(cmd);
3847 cm = cmd[i];
3848 if (strchr(":\024", cm))
3849 goto cd0; // dont allow colon or ctrl-T commands
3850 readbuffer[rbi++] = cm; // put cmd into input buffer
3851
3852 // now we have the command-
3853 // there are 1, 2, and multi char commands
3854 // find out which and generate the rest of command as necessary
3855 if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
3856 cmd1 = " \n\r0$^-+wWeEbBhjklHL";
3857 if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
3858 cmd1 = "abcdefghijklmnopqrstuvwxyz";
3859 }
3860 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3861 c = cmd1[thing];
3862 readbuffer[rbi++] = c; // add movement to input buffer
3863 }
3864 if (strchr("iIaAsc", cm)) { // multi-char commands
3865 if (cm == 'c') {
3866 // change some thing
3867 thing = (int) lrand48() % strlen(cmd1); // pick a movement command
3868 c = cmd1[thing];
3869 readbuffer[rbi++] = c; // add movement to input buffer
3870 }
3871 thing = (int) lrand48() % 4; // what thing to insert
3872 cnt = (int) lrand48() % 10; // how many to insert
3873 for (i = 0; i < cnt; i++) {
3874 if (thing == 0) { // insert chars
3875 readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
3876 } else if (thing == 1) { // insert words
3877 strcat((char *) readbuffer, words[(int) lrand48() % 20]);
3878 strcat((char *) readbuffer, " ");
3879 sleeptime = 0; // how fast to type
3880 } else if (thing == 2) { // insert lines
3881 strcat((char *) readbuffer, lines[(int) lrand48() % 20]);
3882 sleeptime = 0; // how fast to type
3883 } else { // insert multi-lines
3884 strcat((char *) readbuffer, multilines[(int) lrand48() % 20]);
3885 sleeptime = 0; // how fast to type
3886 }
3887 }
3888 strcat((char *) readbuffer, "\033");
3889 }
3890 readed_for_parse = strlen(readbuffer);
3891 cd1:
3892 totalcmds++;
3893 if (sleeptime > 0)
3894 (void) mysleep(sleeptime); // sleep 1/100 sec
3895}
3896
3897// test to see if there are any errors
3898static void crash_test()
3899{
3900 static time_t oldtim;
3901 time_t tim;
3902 char d[2], msg[BUFSIZ];
3903
3904 msg[0] = '\0';
3905 if (end < text) {
3906 strcat((char *) msg, "end<text ");
3907 }
3908 if (end > textend) {
3909 strcat((char *) msg, "end>textend ");
3910 }
3911 if (dot < text) {
3912 strcat((char *) msg, "dot<text ");
3913 }
3914 if (dot > end) {
3915 strcat((char *) msg, "dot>end ");
3916 }
3917 if (screenbegin < text) {
3918 strcat((char *) msg, "screenbegin<text ");
3919 }
3920 if (screenbegin > end - 1) {
3921 strcat((char *) msg, "screenbegin>end-1 ");
3922 }
3923
3924 if (strlen(msg) > 0) {
3925 alarm(0);
3926 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
3927 totalcmds, last_input_char, msg, SOs, SOn);
3928 fflush(stdout);
3929 while (read(0, d, 1) > 0) {
3930 if (d[0] == '\n' || d[0] == '\r')
3931 break;
3932 }
3933 alarm(3);
3934 }
3935 tim = (time_t) time((time_t *) 0);
3936 if (tim >= (oldtim + 3)) {
3937 sprintf((char *) status_buffer,
3938 "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
3939 totalcmds, M, N, I, D, Y, P, U, end - text + 1);
3940 oldtim = tim;
3941 }
3942 return;
3943}
3944#endif /* CONFIG_FEATURE_VI_CRASHME */
Note: See TracBrowser for help on using the repository browser.