source: MondoRescue/branches/stable/mindi-busybox/editors/vi.c

Last change on this file was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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