1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1982-2014 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * David Korn <dgkorn@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /* Adapted for ksh by David Korn */
22 /*+ VI.C P.D. Sullivan
23 *
24 * One line editor for the shell based on the vi editor.
25 *
26 * Questions to:
27 * P.D. Sullivan
28 * cbosgd!pds
29 -*/
30
31
32 #if KSHELL
33 # include "defs.h"
34 #else
35 # include <ast.h>
36 # include "FEATURE/options"
37 # include <ctype.h>
38 #endif /* KSHELL */
39 #include "io.h"
40
41 #include "history.h"
42 #include "edit.h"
43 #include "terminal.h"
44 #include "FEATURE/time"
45
46 #ifdef ECHOCTL
47 # define echoctl ECHOCTL
48 #else
49 # define echoctl 0
50 #endif /* ECHOCTL */
51
52 #ifndef FIORDCHK
53 # define NTICKS 5 /* number of ticks for typeahead */
54 #endif /* FIORDCHK */
55
56 #define MAXCHAR MAXLINE-2 /* max char per line */
57
58 #if SHOPT_MULTIBYTE
59 # include "lexstates.h"
60 # define gencpy(a,b) ed_gencpy(a,b)
61 # define genncpy(a,b,n) ed_genncpy(a,b,n)
62 # define genlen(str) ed_genlen(str)
63 # define digit(c) ((c&~STRIP)==0 && isdigit(c))
64 # define is_print(c) ((c&~STRIP) || isprint(c))
65 # if !_lib_iswprint && !defined(iswprint)
66 # define iswprint(c) ((c&~0177) || isprint(c))
67 # endif
68 static int _isalph(int);
69 static int _ismetach(int);
70 static int _isblank(int);
71 # undef isblank
72 # define isblank(v) _isblank(virtual[v])
73 # define isalph(v) _isalph(virtual[v])
74 # define ismetach(v) _ismetach(virtual[v])
75 #else
76 static genchar _c;
77 # define gencpy(a,b) strcpy((char*)(a),(char*)(b))
78 # define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n)
79 # define genlen(str) strlen(str)
80 # define isalph(v) ((_c=virtual[v])=='_'||isalnum(_c))
81 # undef isblank
82 # define isblank(v) isspace(virtual[v])
83 # define ismetach(v) ismeta(virtual[v])
84 # define digit(c) isdigit(c)
85 # define is_print(c) isprint(c)
86 #endif /* SHOPT_MULTIBYTE */
87
88 #if ( 'a' == 97) /* ASCII? */
89 # define fold(c) ((c)&~040) /* lower and uppercase equivalent */
90 #else
91 # define fold(c) ((c)|0100) /* lower and uppercase equivalent */
92 #endif
93
94 #ifndef iswascii
95 #define iswascii(c) (!((c)&(~0177)))
96 #endif
97
98 typedef struct _vi_
99 {
100 int direction;
101 int lastmacro;
102 char addnl; /* boolean - add newline flag */
103 char last_find; /* last find command */
104 char last_cmd; /* last command */
105 char repeat_set;
106 char nonewline;
107 int findchar; /* last find char */
108 genchar *lastline;
109 int first_wind; /* first column of window */
110 int last_wind; /* last column in window */
111 int lastmotion; /* last motion */
112 int long_char; /* line bigger than window */
113 int long_line; /* line bigger than window */
114 int ocur_phys; /* old current physical position */
115 int ocur_virt; /* old last virtual position */
116 int ofirst_wind; /* old window first col */
117 int o_v_char; /* prev virtual[ocur_virt] */
118 int repeat; /* repeat count for motion cmds */
119 int lastrepeat; /* last repeat count for motion cmds */
120 int u_column; /* undo current column */
121 int U_saved; /* original virtual saved */
122 genchar *U_space; /* used for U command */
123 genchar *u_space; /* used for u command */
124 #ifdef FIORDCHK
125 clock_t typeahead; /* typeahead occurred */
126 #else
127 int typeahead; /* typeahead occurred */
128 #endif /* FIORDCHK */
129 #if SHOPT_MULTIBYTE
130 int bigvi;
131 #endif
132 Edit_t *ed; /* pointer to edit data */
133 } Vi_t;
134
135 #define editb (*vp->ed)
136
137 #undef putchar
138 #define putchar(c) ed_putchar(vp->ed,c)
139
140 #define crallowed editb.e_crlf
141 #define cur_virt editb.e_cur /* current virtual column */
142 #define cur_phys editb.e_pcur /* current phys column cursor is at */
143 #define curhline editb.e_hline /* current history line */
144 #define first_virt editb.e_fcol /* first allowable column */
145 #define globals editb.e_globals /* local global variables */
146 #define histmin editb.e_hismin
147 #define histmax editb.e_hismax
148 #define last_phys editb.e_peol /* last column in physical */
149 #define last_virt editb.e_eol /* last column */
150 #define lsearch editb.e_search /* last search string */
151 #define lookahead editb.e_lookahead /* characters in buffer */
152 #define previous editb.e_lbuf /* lookahead buffer */
153 #define max_col editb.e_llimit /* maximum column */
154 #define Prompt editb.e_prompt /* pointer to prompt */
155 #define plen editb.e_plen /* length of prompt */
156 #define physical editb.e_physbuf /* physical image */
157 #define usreof editb.e_eof /* user defined eof char */
158 #define usrerase editb.e_erase /* user defined erase char */
159 #define usrlnext editb.e_lnext /* user defined next literal */
160 #define usrkill editb.e_kill /* user defined kill char */
161 #define virtual editb.e_inbuf /* pointer to virtual image buffer */
162 #define window editb.e_window /* window buffer */
163 #define w_size editb.e_wsize /* window size */
164 #define inmacro editb.e_inmacro /* true when in macro */
165 #define yankbuf editb.e_killbuf /* yank/delete buffer */
166
167
168 #define ABORT -2 /* user abort */
169 #define APPEND -10 /* append chars */
170 #define BAD -1 /* failure flag */
171 #define BIGVI -15 /* user wants real vi */
172 #define CONTROL -20 /* control mode */
173 #define ENTER -25 /* enter flag */
174 #define GOOD 0 /* success flag */
175 #define INPUT -30 /* input mode */
176 #define INSERT -35 /* insert mode */
177 #define REPLACE -40 /* replace chars */
178 #define SEARCH -45 /* search flag */
179 #define TRANSLATE -50 /* translate virt to phys only */
180
181 #define INVALID (-1) /* invalid column */
182
183 static const char paren_chars[] = "([{)]}"; /* for % command */
184
185 static void cursor(Vi_t*, int);
186 static void del_line(Vi_t*,int);
187 static int getcount(Vi_t*,int);
188 static void vigetline(Vi_t*,int);
189 static int getrchar(Vi_t*);
190 static int mvcursor(Vi_t*,int);
191 static void pr_string(Vi_t*,const char*);
192 static void putstring(Vi_t*,int, int);
193 static void refresh(Vi_t*,int);
194 static void replace(Vi_t*,int, int);
195 static void restore_v(Vi_t*);
196 static void save_last(Vi_t*);
197 static void save_v(Vi_t*);
198 static int search(Vi_t*,int);
199 static void sync_cursor(Vi_t*);
200 static int textmod(Vi_t*,int,int);
201
202 /*+ VI_READ( fd, shbuf, nchar )
203 *
204 * This routine implements a one line version of vi and is
205 * called by _filbuf.c
206 *
207 -*/
208
209 /*
210 * if reedit is non-zero, initialize edit buffer with reedit chars
211 */
ed_viread(void * context,int fd,register char * shbuf,int nchar,int reedit)212 int ed_viread(void *context, int fd, register char *shbuf, int nchar, int reedit)
213 {
214 Edit_t *ed = (Edit_t*)context;
215 register int i; /* general variable */
216 register int term_char=0; /* read() termination character */
217 register Vi_t *vp = ed->e_vi;
218 char prompt[PRSIZE+2]; /* prompt */
219 genchar Physical[2*MAXLINE]; /* physical image */
220 genchar Ubuf[MAXLINE]; /* used for U command */
221 genchar ubuf[MAXLINE]; /* used for u command */
222 genchar Window[MAXLINE]; /* window image */
223 int Globals[9]; /* local global variables */
224 int esc_or_hang=0; /* <ESC> or hangup */
225 char cntl_char=0; /* TRUE if control character present */
226 #if SHOPT_RAWONLY
227 # define viraw 1
228 #else
229 int viraw = (sh_isoption(ed->sh,SH_VIRAW) || ed->sh->st.trap[SH_KEYTRAP]);
230 # ifndef FIORDCHK
231 clock_t oldtime, newtime;
232 struct tms dummy;
233 # endif /* FIORDCHK */
234 #endif /* SHOPT_RAWONLY */
235 if(!vp)
236 {
237 ed->e_vi = vp = newof(0,Vi_t,1,0);
238 vp->lastline = (genchar*)malloc(MAXLINE*CHARSIZE);
239 vp->direction = -1;
240 vp->ed = ed;
241 }
242
243 /*** setup prompt ***/
244
245 Prompt = prompt;
246 ed_setup(vp->ed,fd, reedit);
247 shbuf[reedit] = 0;
248
249 #if !SHOPT_RAWONLY
250 if(!viraw)
251 {
252 /*** Change the eol characters to '\r' and eof ***/
253 /* in addition to '\n' and make eof an ESC */
254 if(tty_alt(ERRIO) < 0)
255 return(reexit?reedit:ed_read(context, fd, shbuf, nchar,0));
256
257 #ifdef FIORDCHK
258 ioctl(fd,FIORDCHK,&vp->typeahead);
259 #else
260 /* time the current line to determine typeahead */
261 oldtime = times(&dummy);
262 #endif /* FIORDCHK */
263 #if KSHELL
264 /* abort of interrupt has occurred */
265 if(ed->sh->trapnote&SH_SIGSET)
266 i = -1;
267 else
268 #endif /* KSHELL */
269 /*** Read the line ***/
270 i = ed_read(context, fd, shbuf, nchar, 0);
271 #ifndef FIORDCHK
272 newtime = times(&dummy);
273 vp->typeahead = ((newtime-oldtime) < NTICKS);
274 #endif /* FIORDCHK */
275 if(echoctl)
276 {
277 if( i <= 0 )
278 {
279 /*** read error or eof typed ***/
280 tty_cooked(ERRIO);
281 return(i);
282 }
283 term_char = shbuf[--i];
284 if( term_char == '\r' )
285 term_char = '\n';
286 if( term_char=='\n' || term_char==ESC )
287 shbuf[i--] = '\0';
288 else
289 shbuf[i+1] = '\0';
290 }
291 else
292 {
293 register int c = shbuf[0];
294
295 /*** Save and remove the last character if its an eol, ***/
296 /* changing '\r' to '\n' */
297
298 if( i == 0 )
299 {
300 /*** ESC was typed as first char of line ***/
301 esc_or_hang = 1;
302 term_char = ESC;
303 shbuf[i--] = '\0'; /* null terminate line */
304 }
305 else if( i<0 || c==usreof )
306 {
307 /*** read error or eof typed ***/
308 tty_cooked(ERRIO);
309 if( c == usreof )
310 i = 0;
311 return(i);
312 }
313 else
314 {
315 term_char = shbuf[--i];
316 if( term_char == '\r' )
317 term_char = '\n';
318 #if !defined(VEOL2) && !defined(ECHOCTL)
319 if(term_char=='\n')
320 {
321 tty_cooked(ERRIO);
322 return(i+1);
323 }
324 #endif
325 if( term_char=='\n' || term_char==usreof )
326 {
327 /*** remove terminator & null terminate ***/
328 shbuf[i--] = '\0';
329 }
330 else
331 {
332 /** terminator was ESC, which is not xmitted **/
333 term_char = ESC;
334 shbuf[i+1] = '\0';
335 }
336 }
337 }
338 }
339 else
340 #endif /* SHOPT_RAWONLY */
341 {
342 /*** Set raw mode ***/
343
344 #if !SHOPT_RAWONLY
345 if( editb.e_ttyspeed == 0 )
346 {
347 /*** never did TCGETA, so do it ***/
348 /* avoids problem if user does 'sh -o viraw' */
349 tty_alt(ERRIO);
350 }
351 #endif /* SHOPT_RAWONLY */
352 if(tty_raw(ERRIO,0) < 0 )
353 return(reedit?reedit:ed_read(context, fd, shbuf, nchar,0));
354 i = last_virt-1;
355 }
356
357 /*** Initialize some things ***/
358
359 virtual = (genchar*)shbuf;
360 #if SHOPT_MULTIBYTE
361 virtual = (genchar*)roundof((char*)virtual-(char*)0,sizeof(genchar));
362 shbuf[i+1] = 0;
363 i = ed_internal(shbuf,virtual)-1;
364 #endif /* SHOPT_MULTIBYTE */
365 globals = Globals;
366 cur_phys = i + 1;
367 cur_virt = i;
368 first_virt = 0;
369 vp->first_wind = 0;
370 last_virt = i;
371 last_phys = i;
372 vp->last_wind = i;
373 vp->long_line = ' ';
374 vp->long_char = ' ';
375 vp->o_v_char = '\0';
376 vp->ocur_phys = 0;
377 vp->ocur_virt = MAXCHAR;
378 vp->ofirst_wind = 0;
379 physical = Physical;
380 vp->u_column = INVALID - 1;
381 vp->U_space = Ubuf;
382 vp->u_space = ubuf;
383 window = Window;
384 window[0] = '\0';
385
386 if(!yankbuf)
387 yankbuf = (genchar*)malloc(MAXLINE*CHARSIZE);
388 if( vp->last_cmd == '\0' )
389 {
390 /*** first time for this shell ***/
391
392 vp->last_cmd = 'i';
393 vp->findchar = INVALID;
394 vp->lastmotion = '\0';
395 vp->lastrepeat = 1;
396 vp->repeat = 1;
397 *yankbuf = 0;
398 }
399
400 /*** fiddle around with prompt length ***/
401 if( nchar+plen > MAXCHAR )
402 nchar = MAXCHAR - plen;
403 max_col = nchar - 2;
404
405 if( !viraw )
406 {
407 int kill_erase = 0;
408 for(i=(echoctl?last_virt:0); i<last_virt; ++i )
409 {
410 /*** change \r to \n, check for control characters, ***/
411 /* delete appropriate ^Vs, */
412 /* and estimate last physical column */
413
414 if( virtual[i] == '\r' )
415 virtual[i] = '\n';
416 if(!echoctl)
417 {
418 register int c = virtual[i];
419 if( c<=usrerase)
420 {
421 /*** user typed escaped erase or kill char ***/
422 cntl_char = 1;
423 if(is_print(c))
424 kill_erase++;
425 }
426 else if( !is_print(c) )
427 {
428 cntl_char = 1;
429
430 if( c == usrlnext )
431 {
432 if( i == last_virt )
433 {
434 /*** eol/eof was escaped ***/
435 /* so replace ^V with it */
436 virtual[i] = term_char;
437 break;
438 }
439
440 /*** delete ^V ***/
441 gencpy((&virtual[i]), (&virtual[i+1]));
442 --cur_virt;
443 --last_virt;
444 }
445 }
446 }
447 }
448
449 /*** copy virtual image to window ***/
450 if(last_virt > 0)
451 last_phys = ed_virt_to_phys(vp->ed,virtual,physical,last_virt,0,0);
452 if( last_phys >= w_size )
453 {
454 /*** line longer than window ***/
455 vp->last_wind = w_size - 1;
456 }
457 else
458 vp->last_wind = last_phys;
459 genncpy(window, virtual, vp->last_wind+1);
460
461 if( term_char!=ESC && (last_virt==INVALID
462 || virtual[last_virt]!=term_char) )
463 {
464 /*** Line not terminated with ESC or escaped (^V) ***/
465 /* eol, so return after doing a total update */
466 /* if( (speed is greater or equal to 1200 */
467 /* and something was typed) and */
468 /* (control character present */
469 /* or typeahead occurred) ) */
470
471 tty_cooked(ERRIO);
472 if( editb.e_ttyspeed==FAST && last_virt!=INVALID
473 && (vp->typeahead || cntl_char) )
474 {
475 refresh(vp,TRANSLATE);
476 pr_string(vp,Prompt);
477 putstring(vp,0, last_phys+1);
478 if(echoctl)
479 ed_crlf(vp->ed);
480 else
481 while(kill_erase-- > 0)
482 putchar(' ');
483 }
484
485 if( term_char=='\n' )
486 {
487 if(!echoctl)
488 ed_crlf(vp->ed);
489 virtual[++last_virt] = '\n';
490 }
491 vp->last_cmd = 'i';
492 save_last(vp);
493 #if SHOPT_MULTIBYTE
494 virtual[last_virt+1] = 0;
495 last_virt = ed_external(virtual,shbuf);
496 return(last_virt);
497 #else
498 return(++last_virt);
499 #endif /* SHOPT_MULTIBYTE */
500 }
501
502 /*** Line terminated with escape, or escaped eol/eof, ***/
503 /* so set raw mode */
504
505 if( tty_raw(ERRIO,0) < 0 )
506 {
507 tty_cooked(ERRIO);
508 /*
509 * The following prevents drivers that return 0 on
510 * causing an infinite loop
511 */
512 if(esc_or_hang)
513 return(-1);
514 virtual[++last_virt] = '\n';
515 #if SHOPT_MULTIBYTE
516 virtual[last_virt+1] = 0;
517 last_virt = ed_external(virtual,shbuf);
518 return(last_virt);
519 #else
520 return(++last_virt);
521 #endif /* SHOPT_MULTIBYTE */
522 }
523
524 if(echoctl) /*** for cntl-echo erase the ^[ ***/
525 pr_string(vp,"\b\b\b\b \b\b");
526
527
528 if(crallowed)
529 {
530 /*** start over since there may be ***/
531 /*** a control char, or cursor might not ***/
532 /*** be at left margin (this lets us know ***/
533 /*** where we are ***/
534 cur_phys = 0;
535 window[0] = '\0';
536 pr_string(vp,Prompt);
537 if( term_char==ESC && (last_virt<0 || virtual[last_virt]!=ESC))
538 refresh(vp,CONTROL);
539 else
540 refresh(vp,INPUT);
541 }
542 else
543 {
544 /*** just update everything internally ***/
545 refresh(vp,TRANSLATE);
546 }
547 }
548
549 /*** Handle usrintr, usrquit, or EOF ***/
550
551 i = sigsetjmp(editb.e_env,0);
552 if( i != 0 )
553 {
554 if(vp->ed->e_multiline)
555 {
556 cur_virt = last_virt;
557 sync_cursor(vp);
558 }
559 virtual[0] = '\0';
560 tty_cooked(ERRIO);
561
562 switch(i)
563 {
564 case UEOF:
565 /*** EOF ***/
566 return(0);
567
568 case UINTR:
569 /** interrupt **/
570 return(-1);
571 }
572 return(-1);
573 }
574
575 /*** Get a line from the terminal ***/
576
577 vp->U_saved = 0;
578 if(reedit)
579 {
580 cur_phys = vp->first_wind;
581 vp->ofirst_wind = INVALID;
582 refresh(vp,INPUT);
583 }
584 if(viraw)
585 vigetline(vp,APPEND);
586 else if(last_virt>=0 && virtual[last_virt]==term_char)
587 vigetline(vp,APPEND);
588 else
589 vigetline(vp,ESC);
590 if(vp->ed->e_multiline)
591 cursor(vp, last_phys);
592 /*** add a new line if user typed unescaped \n ***/
593 /* to cause the shell to process the line */
594 tty_cooked(ERRIO);
595 if(ed->e_nlist)
596 {
597 ed->e_nlist = 0;
598 stkset(ed->sh->stk,ed->e_stkptr,ed->e_stkoff);
599 }
600 if( vp->addnl )
601 {
602 virtual[++last_virt] = '\n';
603 ed_crlf(vp->ed);
604 }
605 if( ++last_virt >= 0 )
606 {
607 #if SHOPT_MULTIBYTE
608 if(vp->bigvi)
609 {
610 vp->bigvi = 0;
611 shbuf[last_virt-1] = '\n';
612 }
613 else
614 {
615 virtual[last_virt] = 0;
616 last_virt = ed_external(virtual,shbuf);
617 }
618 #endif /* SHOPT_MULTIBYTE */
619 #if SHOPT_EDPREDICT
620 if(vp->ed->nhlist)
621 ed_histlist(vp->ed,0);
622 #endif /* SHOPT_EDPREDICT */
623 return(last_virt);
624 }
625 else
626 return(-1);
627 }
628
629
630 /*{ APPEND( char, mode )
631 *
632 * This routine will append char after cur_virt in the virtual image.
633 * mode = APPEND, shift chars right before appending
634 * REPLACE, replace char if possible
635 *
636 }*/
637
append(Vi_t * vp,int c,int mode)638 static void append(Vi_t *vp,int c, int mode)
639 {
640 register int i,j;
641
642 if( last_virt<max_col && last_phys<max_col )
643 {
644 if( mode==APPEND || (cur_virt==last_virt && last_virt>=0))
645 {
646 j = (cur_virt>=0?cur_virt:0);
647 for(i = ++last_virt; i > j; --i)
648 virtual[i] = virtual[i-1];
649 }
650 virtual[++cur_virt] = c;
651 }
652 else
653 ed_ringbell();
654 return;
655 }
656
657 /*{ BACKWORD( nwords, cmd )
658 *
659 * This routine will position cur_virt at the nth previous word.
660 *
661 }*/
662
backword(Vi_t * vp,int nwords,register int cmd)663 static void backword(Vi_t *vp,int nwords, register int cmd)
664 {
665 register int tcur_virt = cur_virt;
666 while( nwords-- && tcur_virt > first_virt )
667 {
668 if( !isblank(tcur_virt) && isblank(tcur_virt-1)
669 && tcur_virt>first_virt )
670 --tcur_virt;
671 else if(cmd != 'B')
672 {
673 register int last = isalph(tcur_virt-1);
674 register int cur = isalph(tcur_virt);
675 if((!cur && last) || (cur && !last))
676 --tcur_virt;
677 }
678 while( isblank(tcur_virt) && tcur_virt>=first_virt )
679 --tcur_virt;
680 if( cmd == 'B' )
681 {
682 while( !isblank(tcur_virt) && tcur_virt>=first_virt )
683 --tcur_virt;
684 }
685 else
686 {
687 if(isalph(tcur_virt))
688 while( isalph(tcur_virt) && tcur_virt>=first_virt )
689 --tcur_virt;
690 else
691 while( !isalph(tcur_virt) && !isblank(tcur_virt)
692 && tcur_virt>=first_virt )
693 --tcur_virt;
694 }
695 cur_virt = ++tcur_virt;
696 }
697 return;
698 }
699
700 /*{ CNTLMODE()
701 *
702 * This routine implements the vi command subset.
703 * The cursor will always be positioned at the char of interest.
704 *
705 }*/
706
cntlmode(Vi_t * vp)707 static int cntlmode(Vi_t *vp)
708 {
709 register int c;
710 register int i;
711 genchar tmp_u_space[MAXLINE]; /* temporary u_space */
712 genchar *real_u_space; /* points to real u_space */
713 int tmp_u_column = INVALID; /* temporary u_column */
714 int was_inmacro;
715
716 if(!vp->U_saved)
717 {
718 /*** save virtual image if never done before ***/
719 virtual[last_virt+1] = '\0';
720 gencpy(vp->U_space, virtual);
721 vp->U_saved = 1;
722 }
723
724 save_last(vp);
725
726 real_u_space = vp->u_space;
727 curhline = histmax;
728 first_virt = 0;
729 vp->repeat = 1;
730 if( cur_virt > INVALID )
731 {
732 /*** make sure cursor is at the last char ***/
733 sync_cursor(vp);
734 }
735 else if(last_virt > INVALID )
736 cur_virt++;
737
738 /*** Read control char until something happens to cause a ***/
739 /* return to APPEND/REPLACE mode */
740
741 while( c=ed_getchar(vp->ed,-1) )
742 {
743 vp->repeat_set = 0;
744 was_inmacro = inmacro;
745 if( c == '0' )
746 {
747 /*** move to leftmost column ***/
748 cur_virt = 0;
749 sync_cursor(vp);
750 continue;
751 }
752
753 if( digit(c) )
754 {
755 c = getcount(vp,c);
756 if( c == '.' )
757 vp->lastrepeat = vp->repeat;
758 }
759
760 /*** see if it's a move cursor command ***/
761
762 if(mvcursor(vp,c))
763 {
764 sync_cursor(vp);
765 vp->repeat = 1;
766 continue;
767 }
768
769 /*** see if it's a repeat of the last command ***/
770
771 if( c == '.' )
772 {
773 c = vp->last_cmd;
774 vp->repeat = vp->lastrepeat;
775 i = textmod(vp,c, c);
776 }
777 else
778 {
779 i = textmod(vp,c, 0);
780 }
781
782 /*** see if it's a text modification command ***/
783
784 switch(i)
785 {
786 case BAD:
787 break;
788
789 default: /** input mode **/
790 if(!was_inmacro)
791 {
792 vp->last_cmd = c;
793 vp->lastrepeat = vp->repeat;
794 }
795 vp->repeat = 1;
796 if( i == GOOD )
797 continue;
798 return(i);
799 }
800
801 switch( c )
802 {
803 /***** Other stuff *****/
804
805 case cntl('L'): /** Redraw line **/
806 /*** print the prompt and ***/
807 /* force a total refresh */
808 if(vp->nonewline==0 && !vp->ed->e_nocrnl)
809 putchar('\n');
810 vp->nonewline = 0;
811 pr_string(vp,Prompt);
812 window[0] = '\0';
813 cur_phys = vp->first_wind;
814 vp->ofirst_wind = INVALID;
815 vp->long_line = ' ';
816 break;
817
818 case cntl('V'):
819 {
820 register const char *p = fmtident(e_version);
821 save_v(vp);
822 del_line(vp,BAD);
823 while(c = *p++)
824 append(vp,c,APPEND);
825 refresh(vp,CONTROL);
826 ed_getchar(vp->ed,-1);
827 restore_v(vp);
828 ed_ungetchar(vp->ed,'a');
829 break;
830 }
831
832 case '/': /** Search **/
833 case '?':
834 case 'N':
835 case 'n':
836 save_v(vp);
837 switch( search(vp,c) )
838 {
839 case GOOD:
840 /*** force a total refresh ***/
841 window[0] = '\0';
842 goto newhist;
843
844 case BAD:
845 /*** no match ***/
846 ed_ringbell();
847
848 default:
849 if( vp->u_column == INVALID )
850 del_line(vp,BAD);
851 else
852 restore_v(vp);
853 break;
854 }
855 break;
856
857 case 'j': /** get next command **/
858 case '+': /** get next command **/
859 #if SHOPT_EDPREDICT
860 if(vp->ed->hlist)
861 {
862 if(vp->ed->hoff >= vp->ed->hmax)
863 goto ringbell;
864 vp->ed->hoff++;
865 goto hupdate;
866 }
867 #endif /* SHOPT_EDPREDICT */
868 curhline += vp->repeat;
869 if( curhline > histmax )
870 {
871 curhline = histmax;
872 goto ringbell;
873 }
874 else if(curhline==histmax && tmp_u_column!=INVALID )
875 {
876 vp->u_space = tmp_u_space;
877 vp->u_column = tmp_u_column;
878 restore_v(vp);
879 vp->u_space = real_u_space;
880 break;
881 }
882 save_v(vp);
883 cur_virt = INVALID;
884 goto newhist;
885
886 case 'k': /** get previous command **/
887 case '-': /** get previous command **/
888 #if SHOPT_EDPREDICT
889 if(vp->ed->hlist)
890 {
891 if(vp->ed->hoff == 0)
892 goto ringbell;
893 vp->ed->hoff--;
894 hupdate:
895 ed_histlist(vp->ed,*vp->ed->hlist!=0);
896 vp->nonewline++;
897 ed_ungetchar(vp->ed,cntl('L'));
898 continue;
899 }
900 #endif /* SHOPT_EDPREDICT */
901 if( curhline == histmax )
902 {
903 vp->u_space = tmp_u_space;
904 i = vp->u_column;
905 save_v(vp);
906 vp->u_space = real_u_space;
907 tmp_u_column = vp->u_column;
908 vp->u_column = i;
909 }
910
911 curhline -= vp->repeat;
912 if( curhline <= histmin )
913 {
914 curhline += vp->repeat;
915 goto ringbell;
916 }
917 save_v(vp);
918 cur_virt = INVALID;
919 newhist:
920 if(curhline!=histmax || cur_virt==INVALID)
921 hist_copy((char*)virtual, MAXLINE, curhline,-1);
922 else
923 {
924 strcpy((char*)virtual,(char*)vp->u_space);
925 #if SHOPT_MULTIBYTE
926 ed_internal((char*)vp->u_space,vp->u_space);
927 #endif /* SHOPT_MULTIBYTE */
928 }
929 #if SHOPT_MULTIBYTE
930 ed_internal((char*)virtual,virtual);
931 #endif /* SHOPT_MULTIBYTE */
932 if((last_virt=genlen(virtual)-1) >= 0 && cur_virt == INVALID)
933 cur_virt = 0;
934 #if SHOPT_EDPREDICT
935 if(vp->ed->hlist)
936 {
937 ed_histlist(vp->ed,0);
938 if(c=='\n')
939 ed_ungetchar(vp->ed,c);
940 ed_ungetchar(vp->ed,cntl('L'));
941 vp->nonewline = 1;
942 cur_virt = 0;
943 }
944 #endif /*SHOPT_EDPREDICT */
945 break;
946
947
948 case 'u': /** undo the last thing done **/
949 restore_v(vp);
950 break;
951
952 case 'U': /** Undo everything **/
953 save_v(vp);
954 if( virtual[0] == '\0' )
955 goto ringbell;
956 else
957 {
958 gencpy(virtual, vp->U_space);
959 last_virt = genlen(vp->U_space) - 1;
960 cur_virt = 0;
961 }
962 break;
963
964 #if KSHELL
965 case 'v':
966 if(vp->repeat_set==0)
967 goto vcommand;
968 #endif /* KSHELL */
969
970 case 'G': /** goto command repeat **/
971 if(vp->repeat_set==0)
972 vp->repeat = histmin+1;
973 if( vp->repeat <= histmin || vp->repeat > histmax )
974 {
975 goto ringbell;
976 }
977 curhline = vp->repeat;
978 save_v(vp);
979 if(c == 'G')
980 {
981 cur_virt = INVALID;
982 goto newhist;
983 }
984
985 #if KSHELL
986 vcommand:
987 if(ed_fulledit(vp->ed)==GOOD)
988 return(BIGVI);
989 else
990 goto ringbell;
991 #endif /* KSHELL */
992
993 case '#': /** insert(delete) # to (no)comment command **/
994 if( cur_virt != INVALID )
995 {
996 register genchar *p = &virtual[last_virt+1];
997 *p = 0;
998 /*** see whether first char is comment char ***/
999 c = (virtual[0]=='#');
1000 while(p-- >= virtual)
1001 {
1002 if(*p=='\n' || p<virtual)
1003 {
1004 if(c) /* delete '#' */
1005 {
1006 if(p[1]=='#')
1007 {
1008 last_virt--;
1009 gencpy(p+1,p+2);
1010 }
1011 }
1012 else
1013 {
1014 cur_virt = p-virtual;
1015 append(vp,'#', APPEND);
1016 }
1017 }
1018 }
1019 if(c)
1020 {
1021 curhline = histmax;
1022 cur_virt = 0;
1023 break;
1024 }
1025 refresh(vp,INPUT);
1026 }
1027
1028 case '\n': /** send to shell **/
1029 #if SHOPT_EDPREDICT
1030 if(!vp->ed->hlist)
1031 return(ENTER);
1032 case '\t': /** bring choice to edit **/
1033 if(vp->ed->hlist)
1034 {
1035 if(vp->repeat > vp->ed->nhlist-vp->ed->hoff)
1036 goto ringbell;
1037 curhline = vp->ed->hlist[vp->repeat+vp->ed->hoff-1]->index;
1038 goto newhist;
1039 }
1040 goto ringbell;
1041 #else
1042 return(ENTER);
1043 #endif /* SHOPT_EDPREDICT */
1044 case ESC:
1045 /* don't ring bell if next char is '[' */
1046 if(!lookahead)
1047 {
1048 char x;
1049 if(sfpkrd(editb.e_fd,&x,1,'\r',400L,-1)>0)
1050 ed_ungetchar(vp->ed,x);
1051 }
1052 if(lookahead)
1053 {
1054 ed_ungetchar(vp->ed,c=ed_getchar(vp->ed,1));
1055 if(c=='[' || c=='O')
1056 {
1057 vp->repeat = 1;
1058 continue;
1059 }
1060 }
1061 default:
1062 ringbell:
1063 ed_ringbell();
1064 vp->repeat = 1;
1065 continue;
1066 }
1067
1068 refresh(vp,CONTROL);
1069 vp->repeat = 1;
1070 }
1071 /* NOTREACHED */
1072 return(0);
1073 }
1074
1075 /*{ CURSOR( new_current_physical )
1076 *
1077 * This routine will position the virtual cursor at
1078 * physical column x in the window.
1079 *
1080 }*/
1081
cursor(Vi_t * vp,register int x)1082 static void cursor(Vi_t *vp,register int x)
1083 {
1084 #if SHOPT_MULTIBYTE
1085 while(physical[x]==MARKER)
1086 x++;
1087 #endif /* SHOPT_MULTIBYTE */
1088 cur_phys = ed_setcursor(vp->ed, physical, cur_phys,x,vp->first_wind);
1089 }
1090
1091 /*{ DELETE( nchars, mode )
1092 *
1093 * Delete nchars from the virtual space and leave cur_virt positioned
1094 * at cur_virt-1.
1095 *
1096 * If mode = 'c', do not save the characters deleted
1097 * = 'd', save them in yankbuf and delete.
1098 * = 'y', save them in yankbuf but do not delete.
1099 *
1100 }*/
1101
cdelete(Vi_t * vp,register int nchars,int mode)1102 static void cdelete(Vi_t *vp,register int nchars, int mode)
1103 {
1104 register int i;
1105 register genchar *cp;
1106
1107 if( cur_virt < first_virt )
1108 {
1109 ed_ringbell();
1110 return;
1111 }
1112 if( nchars > 0 )
1113 {
1114 cp = virtual+cur_virt;
1115 vp->o_v_char = cp[0];
1116 if( (cur_virt-- + nchars) > last_virt )
1117 {
1118 /*** set nchars to number actually deleted ***/
1119 nchars = last_virt - cur_virt;
1120 }
1121
1122 /*** save characters to be deleted ***/
1123
1124 if( mode != 'c' )
1125 {
1126 i = cp[nchars];
1127 cp[nchars] = 0;
1128 gencpy(yankbuf,cp);
1129 cp[nchars] = i;
1130 }
1131
1132 /*** now delete these characters ***/
1133
1134 if( mode != 'y' )
1135 {
1136 gencpy(cp,cp+nchars);
1137 last_virt -= nchars;
1138 }
1139 }
1140 return;
1141 }
1142
1143 /*{ DEL_LINE( mode )
1144 *
1145 * This routine will delete the line.
1146 * mode = GOOD, do a save_v()
1147 *
1148 }*/
del_line(register Vi_t * vp,int mode)1149 static void del_line(register Vi_t *vp, int mode)
1150 {
1151 if( last_virt == INVALID )
1152 return;
1153
1154 if( mode == GOOD )
1155 save_v(vp);
1156
1157 cur_virt = 0;
1158 first_virt = 0;
1159 cdelete(vp,last_virt+1, BAD);
1160 refresh(vp,CONTROL);
1161
1162 cur_virt = INVALID;
1163 cur_phys = 0;
1164 vp->findchar = INVALID;
1165 last_phys = INVALID;
1166 last_virt = INVALID;
1167 vp->last_wind = INVALID;
1168 vp->first_wind = 0;
1169 vp->o_v_char = '\0';
1170 vp->ocur_phys = 0;
1171 vp->ocur_virt = MAXCHAR;
1172 vp->ofirst_wind = 0;
1173 window[0] = '\0';
1174 return;
1175 }
1176
1177 /*{ DELMOTION( motion, mode )
1178 *
1179 * Delete thru motion.
1180 *
1181 * mode = 'd', save deleted characters, delete
1182 * = 'c', do not save characters, change
1183 * = 'y', save characters, yank
1184 *
1185 * Returns 1 if operation successful; else 0.
1186 *
1187 }*/
1188
delmotion(Vi_t * vp,int motion,int mode)1189 static int delmotion(Vi_t *vp,int motion, int mode)
1190 {
1191 register int begin, end, delta;
1192 /* the following saves a register */
1193
1194 if( cur_virt == INVALID )
1195 return(0);
1196 if( mode != 'y' )
1197 save_v(vp);
1198 begin = cur_virt;
1199
1200 /*** fake out the motion routines by appending a blank ***/
1201
1202 virtual[++last_virt] = ' ';
1203 end = mvcursor(vp,motion);
1204 virtual[last_virt--] = 0;
1205 if(!end)
1206 return(0);
1207
1208 end = cur_virt;
1209 if( mode=='c' && end>begin && strchr("wW", motion) )
1210 {
1211 /*** called by change operation, user really expects ***/
1212 /* the effect of the eE commands, so back up to end of word */
1213 while( end>begin && isblank(end-1) )
1214 --end;
1215 if( end == begin )
1216 ++end;
1217 }
1218
1219 delta = end - begin;
1220 if( delta >= 0 )
1221 {
1222 cur_virt = begin;
1223 if( strchr("eE;,TtFf%", motion) )
1224 ++delta;
1225 }
1226 else
1227 {
1228 delta = -delta + (motion=='%');
1229 }
1230
1231 cdelete(vp,delta, mode);
1232 if( mode == 'y' )
1233 cur_virt = begin;
1234 return(1);
1235 }
1236
1237
1238 /*{ ENDWORD( nwords, cmd )
1239 *
1240 * This routine will move cur_virt to the end of the nth word.
1241 *
1242 }*/
1243
endword(Vi_t * vp,int nwords,register int cmd)1244 static void endword(Vi_t *vp, int nwords, register int cmd)
1245 {
1246 register int tcur_virt = cur_virt;
1247 while( nwords-- )
1248 {
1249 if( !isblank(tcur_virt) && tcur_virt<=last_virt )
1250 ++tcur_virt;
1251 while( isblank(tcur_virt) && tcur_virt<=last_virt )
1252 ++tcur_virt;
1253 if( cmd == 'E' )
1254 {
1255 while( !isblank(tcur_virt) && tcur_virt<=last_virt )
1256 ++tcur_virt;
1257 }
1258 else
1259 {
1260 if( isalph(tcur_virt) )
1261 while( isalph(tcur_virt) && tcur_virt<=last_virt )
1262 ++tcur_virt;
1263 else
1264 while( !isalph(tcur_virt) && !isblank(tcur_virt)
1265 && tcur_virt<=last_virt )
1266 ++tcur_virt;
1267 }
1268 if( tcur_virt > first_virt )
1269 tcur_virt--;
1270 }
1271 cur_virt = tcur_virt;
1272 return;
1273 }
1274
1275 /*{ FORWARD( nwords, cmd )
1276 *
1277 * This routine will move cur_virt forward to the next nth word.
1278 *
1279 }*/
1280
forward(Vi_t * vp,register int nwords,int cmd)1281 static void forward(Vi_t *vp,register int nwords, int cmd)
1282 {
1283 register int tcur_virt = cur_virt;
1284 while( nwords-- )
1285 {
1286 if( cmd == 'W' )
1287 {
1288 while( !isblank(tcur_virt) && tcur_virt < last_virt )
1289 ++tcur_virt;
1290 }
1291 else
1292 {
1293 if( isalph(tcur_virt) )
1294 {
1295 while( isalph(tcur_virt) && tcur_virt<last_virt )
1296 ++tcur_virt;
1297 }
1298 else
1299 {
1300 while( !isalph(tcur_virt) && !isblank(tcur_virt)
1301 && tcur_virt < last_virt )
1302 ++tcur_virt;
1303 }
1304 }
1305 while( isblank(tcur_virt) && tcur_virt < last_virt )
1306 ++tcur_virt;
1307 }
1308 cur_virt = tcur_virt;
1309 return;
1310 }
1311
1312
1313
1314 /*{ GETCOUNT(c)
1315 *
1316 * Set repeat to the user typed number and return the terminating
1317 * character.
1318 *
1319 }*/
1320
getcount(register Vi_t * vp,register int c)1321 static int getcount(register Vi_t *vp,register int c)
1322 {
1323 register int i;
1324
1325 /*** get any repeat count ***/
1326
1327 if( c == '0' )
1328 return(c);
1329
1330 vp->repeat_set++;
1331 i = 0;
1332 while( digit(c) )
1333 {
1334 i = i*10 + c - '0';
1335 c = ed_getchar(vp->ed,-1);
1336 }
1337
1338 if( i > 0 )
1339 vp->repeat *= i;
1340 return(c);
1341 }
1342
1343
1344 /*{ GETLINE( mode )
1345 *
1346 * This routine will fetch a line.
1347 * mode = APPEND, allow escape to cntlmode subroutine
1348 * appending characters.
1349 * = REPLACE, allow escape to cntlmode subroutine
1350 * replacing characters.
1351 * = SEARCH, no escape allowed
1352 * = ESC, enter control mode immediately
1353 *
1354 * The cursor will always be positioned after the last
1355 * char printed.
1356 *
1357 * This routine returns when cr, nl, or (eof in column 0) is
1358 * received (column 0 is the first char position).
1359 *
1360 }*/
1361
vigetline(register Vi_t * vp,register int mode)1362 static void vigetline(register Vi_t* vp,register int mode)
1363 {
1364 register int c;
1365 register int tmp;
1366 int max_virt=0, last_save=0;
1367 genchar saveline[MAXLINE];
1368 vp->addnl = 1;
1369
1370 if( mode == ESC )
1371 {
1372 /*** go directly to control mode ***/
1373 goto escape;
1374 }
1375
1376 for(;;)
1377 {
1378 if( (c=ed_getchar(vp->ed,mode==SEARCH?1:-2)) == usreof )
1379 c = UEOF;
1380 else if( c == usrerase )
1381 c = UERASE;
1382 else if( c == usrkill )
1383 c = UKILL;
1384 else if( c == editb.e_werase )
1385 c = UWERASE;
1386 else if( c == usrlnext )
1387 c = ULNEXT;
1388 else if(mode==SEARCH && c==editb.e_intr)
1389 c = UINTR;
1390
1391 if( c == ULNEXT)
1392 {
1393 /*** implement ^V to escape next char ***/
1394 c = ed_getchar(vp->ed,2);
1395 append(vp,c, mode);
1396 refresh(vp,INPUT);
1397 continue;
1398 }
1399 if(c!='\t')
1400 vp->ed->e_tabcount = 0;
1401
1402 switch( c )
1403 {
1404 case ESC: /** enter control mode **/
1405 if(!sh_isoption(vp->ed->sh,SH_VI))
1406 {
1407 append(vp,c, mode);
1408 break;
1409 }
1410 if( mode == SEARCH )
1411 {
1412 ed_ringbell();
1413 continue;
1414 }
1415 else
1416 {
1417 escape:
1418 if( mode == REPLACE )
1419 {
1420 c = max_virt-cur_virt;
1421 if(c > 0 && last_save>=cur_virt)
1422 {
1423 genncpy((&virtual[cur_virt]),&saveline[cur_virt],c);
1424 if(last_virt>=last_save)
1425 last_virt=last_save-1;
1426 refresh(vp,INPUT);
1427 }
1428 --cur_virt;
1429 }
1430 tmp = cntlmode(vp);
1431 if( tmp == ENTER || tmp == BIGVI )
1432 {
1433 #if SHOPT_MULTIBYTE
1434 vp->bigvi = (tmp==BIGVI);
1435 #endif /* SHOPT_MULTIBYTE */
1436 return;
1437 }
1438 if( tmp == INSERT )
1439 {
1440 mode = APPEND;
1441 continue;
1442 }
1443 mode = tmp;
1444 if(mode==REPLACE)
1445 {
1446 c = last_save = last_virt+1;
1447 if(c >= MAXLINE)
1448 c = MAXLINE-1;
1449 genncpy(saveline, virtual, c);
1450 }
1451 }
1452 break;
1453
1454 case UINTR:
1455 first_virt = 0;
1456 cdelete(vp,cur_virt+1, BAD);
1457 cur_virt = -1;
1458 return;
1459 case UERASE: /** user erase char **/
1460 /*** treat as backspace ***/
1461
1462 case '\b': /** backspace **/
1463 if(cur_virt>=0 && virtual[cur_virt] == '\\')
1464 {
1465 cdelete(vp,1, BAD);
1466 append(vp,usrerase, mode);
1467 }
1468 else
1469 {
1470 if( mode==SEARCH && cur_virt==0 )
1471 {
1472 first_virt = 0;
1473 cdelete(vp,1, BAD);
1474 return;
1475 }
1476 if(mode==REPLACE || (last_save>0 && last_virt<=last_save))
1477 {
1478 if(cur_virt<=first_virt)
1479 ed_ringbell();
1480 else if(mode==REPLACE)
1481 --cur_virt;
1482 mode = REPLACE;
1483 sync_cursor(vp);
1484 continue;
1485 }
1486 else
1487 cdelete(vp,1, BAD);
1488 }
1489 break;
1490
1491 case UWERASE: /** delete back word **/
1492 if( cur_virt > first_virt &&
1493 !isblank(cur_virt) &&
1494 !ispunct(virtual[cur_virt]) &&
1495 isblank(cur_virt-1) )
1496 {
1497 cdelete(vp,1, BAD);
1498 }
1499 else
1500 {
1501 tmp = cur_virt;
1502 backword(vp,1, 'W');
1503 cdelete(vp,tmp - cur_virt + 1, BAD);
1504 }
1505 break;
1506
1507 case UKILL: /** user kill line char **/
1508 if( virtual[cur_virt] == '\\' )
1509 {
1510 cdelete(vp,1, BAD);
1511 append(vp,usrkill, mode);
1512 }
1513 else
1514 {
1515 if( mode == SEARCH )
1516 {
1517 cur_virt = 1;
1518 delmotion(vp, '$', BAD);
1519 }
1520 else if(first_virt)
1521 {
1522 tmp = cur_virt;
1523 cur_virt = first_virt;
1524 cdelete(vp,tmp - cur_virt + 1, BAD);
1525 }
1526 else
1527 del_line(vp,GOOD);
1528 }
1529 break;
1530
1531 case UEOF: /** eof char **/
1532 if( cur_virt != INVALID )
1533 continue;
1534 vp->addnl = 0;
1535
1536 case '\n': /** newline or return **/
1537 if( mode != SEARCH )
1538 save_last(vp);
1539 refresh(vp,INPUT);
1540 physical[++last_phys] = 0;
1541 return;
1542
1543 case '\t': /** command completion **/
1544 if(mode!=SEARCH && (last_virt>=0||vp->ed->e_tabcount) && vp->ed->sh->nextprompt)
1545 {
1546 if(virtual[cur_virt]=='\\')
1547 {
1548 virtual[cur_virt] = '\t';
1549 break;
1550 }
1551 if(vp->ed->e_tabcount==0)
1552 {
1553 ed_ungetchar(vp->ed,'\\');
1554 vp->ed->e_tabcount=1;
1555 goto escape;
1556 }
1557 else if(vp->ed->e_tabcount==1)
1558 {
1559 if(last_virt==0)
1560 #if 1
1561 cur_virt = --last_virt;
1562 #endif
1563 ed_ungetchar(vp->ed,'=');
1564 goto escape;
1565 }
1566 vp->ed->e_tabcount = 0;
1567 }
1568 else
1569 vp->ed->e_tabcount = 1;
1570 /* FALL THRU*/
1571 default:
1572 if( mode == REPLACE )
1573 {
1574 if( cur_virt < last_virt )
1575 {
1576 replace(vp,c, 1);
1577 if(cur_virt>max_virt)
1578 max_virt = cur_virt;
1579 continue;
1580 }
1581 cdelete(vp,1, BAD);
1582 mode = APPEND;
1583 max_virt = last_virt+3;
1584 }
1585 append(vp,c, mode);
1586 break;
1587 }
1588 refresh(vp,INPUT);
1589
1590 }
1591 }
1592
1593 /*{ MVCURSOR( motion )
1594 *
1595 * This routine will move the virtual cursor according to motion
1596 * for repeat times.
1597 *
1598 * It returns GOOD if successful; else BAD.
1599 *
1600 }*/
1601
mvcursor(register Vi_t * vp,register int motion)1602 static int mvcursor(register Vi_t* vp,register int motion)
1603 {
1604 register int count;
1605 register int tcur_virt;
1606 register int incr = -1;
1607 register int bound = 0;
1608
1609 switch(motion)
1610 {
1611 /***** Cursor move commands *****/
1612
1613 case '0': /** First column **/
1614 tcur_virt = 0;
1615 break;
1616
1617 case '^': /** First nonblank character **/
1618 tcur_virt = first_virt;
1619 while( isblank(tcur_virt) && tcur_virt < last_virt )
1620 ++tcur_virt;
1621 break;
1622
1623 case '|':
1624 tcur_virt = vp->repeat-1;
1625 if(tcur_virt <= last_virt)
1626 break;
1627 /* fall through */
1628
1629 case '$': /** End of line **/
1630 tcur_virt = last_virt;
1631 break;
1632
1633 case 'O':
1634 case '[':
1635 switch(motion=getcount(vp,ed_getchar(vp->ed,-1)))
1636 {
1637 case 'A':
1638 #if SHOPT_EDPREDICT
1639 if(!vp->ed->hlist && cur_virt>=0 && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt)
1640 #else
1641 if(cur_virt>=0 && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt)
1642 #endif /* SHOPT_EDPREDICT */
1643 {
1644 virtual[last_virt + 1] = '\0';
1645 #if SHOPT_MULTIBYTE
1646 ed_external(virtual,lsearch+1);
1647 #else
1648 strcpy(lsearch+1,virtual);
1649 #endif /* SHOPT_MULTIBYTE */
1650 *lsearch = '^';
1651 vp->direction = -2;
1652 ed_ungetchar(vp->ed,'n');
1653 }
1654 else if(cur_virt==0 && vp->direction == -2)
1655 ed_ungetchar(vp->ed,'n');
1656 else
1657 ed_ungetchar(vp->ed,'k');
1658 return(1);
1659 case 'B':
1660 ed_ungetchar(vp->ed,'j');
1661 return(1);
1662 case 'C':
1663 motion = last_virt;
1664 incr = 1;
1665 goto walk;
1666 case 'D':
1667 motion = first_virt;
1668 goto walk;
1669 case 'H':
1670 tcur_virt = 0;
1671 break;
1672 case 'Y':
1673 tcur_virt = last_virt;
1674 break;
1675 default:
1676 ed_ungetchar(vp->ed,motion);
1677 return(0);
1678 }
1679 break;
1680
1681 case 'h': /** Left one **/
1682 case '\b':
1683 motion = first_virt;
1684 goto walk;
1685
1686 case ' ':
1687 case 'l': /** Right one **/
1688 motion = last_virt;
1689 incr = 1;
1690 walk:
1691 tcur_virt = cur_virt;
1692 if( incr*tcur_virt < motion)
1693 {
1694 tcur_virt += vp->repeat*incr;
1695 if( incr*tcur_virt > motion)
1696 tcur_virt = motion;
1697 }
1698 else
1699 return(0);
1700 break;
1701
1702 case 'B':
1703 case 'b': /** back word **/
1704 tcur_virt = cur_virt;
1705 backword(vp,vp->repeat, motion);
1706 if( cur_virt == tcur_virt )
1707 return(0);
1708 return(1);
1709
1710 case 'E':
1711 case 'e': /** end of word **/
1712 tcur_virt = cur_virt;
1713 if(tcur_virt >=0)
1714 endword(vp, vp->repeat, motion);
1715 if( cur_virt == tcur_virt )
1716 return(0);
1717 return(1);
1718
1719 case ',': /** reverse find old char **/
1720 case ';': /** find old char **/
1721 switch(vp->last_find)
1722 {
1723 case 't':
1724 case 'f':
1725 if(motion==';')
1726 {
1727 bound = last_virt;
1728 incr = 1;
1729 }
1730 goto find_b;
1731
1732 case 'T':
1733 case 'F':
1734 if(motion==',')
1735 {
1736 bound = last_virt;
1737 incr = 1;
1738 }
1739 goto find_b;
1740
1741 default:
1742 return(0);
1743 }
1744
1745
1746 case 't': /** find up to new char forward **/
1747 case 'f': /** find new char forward **/
1748 bound = last_virt;
1749 incr = 1;
1750
1751 case 'T': /** find up to new char backward **/
1752 case 'F': /** find new char backward **/
1753 vp->last_find = motion;
1754 if((vp->findchar=getrchar(vp))==ESC)
1755 return(1);
1756 find_b:
1757 tcur_virt = cur_virt;
1758 count = vp->repeat;
1759 while( count-- )
1760 {
1761 while( incr*(tcur_virt+=incr) <= bound
1762 && virtual[tcur_virt] != vp->findchar );
1763 if( incr*tcur_virt > bound )
1764 {
1765 return(0);
1766 }
1767 }
1768 if( fold(vp->last_find) == 'T' )
1769 tcur_virt -= incr;
1770 break;
1771
1772 case '%':
1773 {
1774 int nextmotion;
1775 int nextc;
1776 tcur_virt = cur_virt;
1777 while( tcur_virt <= last_virt
1778 && strchr(paren_chars,virtual[tcur_virt])==(char*)0)
1779 tcur_virt++;
1780 if(tcur_virt > last_virt )
1781 return(0);
1782 nextc = virtual[tcur_virt];
1783 count = strchr(paren_chars,nextc)-paren_chars;
1784 if(count < 3)
1785 {
1786 incr = 1;
1787 bound = last_virt;
1788 nextmotion = paren_chars[count+3];
1789 }
1790 else
1791 nextmotion = paren_chars[count-3];
1792 count = 1;
1793 while(count >0 && incr*(tcur_virt+=incr) <= bound)
1794 {
1795 if(virtual[tcur_virt] == nextmotion)
1796 count--;
1797 else if(virtual[tcur_virt]==nextc)
1798 count++;
1799 }
1800 if(count)
1801 return(0);
1802 break;
1803 }
1804
1805 case 'W':
1806 case 'w': /** forward word **/
1807 tcur_virt = cur_virt;
1808 forward(vp,vp->repeat, motion);
1809 if( tcur_virt == cur_virt )
1810 return(0);
1811 return(1);
1812
1813 default:
1814 return(0);
1815 }
1816 cur_virt = tcur_virt;
1817
1818 return(1);
1819 }
1820
1821 /*
1822 * print a string
1823 */
1824
pr_string(register Vi_t * vp,register const char * sp)1825 static void pr_string(register Vi_t *vp, register const char *sp)
1826 {
1827 /*** copy string sp ***/
1828 register char *ptr = editb.e_outptr;
1829 while(*sp)
1830 *ptr++ = *sp++;
1831 editb.e_outptr = ptr;
1832 return;
1833 }
1834
1835 /*{ PUTSTRING( column, nchars )
1836 *
1837 * Put nchars starting at column of physical into the workspace
1838 * to be printed.
1839 *
1840 }*/
1841
putstring(register Vi_t * vp,register int col,register int nchars)1842 static void putstring(register Vi_t *vp,register int col, register int nchars)
1843 {
1844 while( nchars-- )
1845 putchar(physical[col++]);
1846 return;
1847 }
1848
1849 /*{ REFRESH( mode )
1850 *
1851 * This routine will refresh the crt so the physical image matches
1852 * the virtual image and display the proper window.
1853 *
1854 * mode = CONTROL, refresh in control mode, ie. leave cursor
1855 * positioned at last char printed.
1856 * = INPUT, refresh in input mode; leave cursor positioned
1857 * after last char printed.
1858 * = TRANSLATE, perform virtual to physical translation
1859 * and adjust left margin only.
1860 *
1861 * +-------------------------------+
1862 * | | | virtual | | |
1863 * +-------------------------------+
1864 * cur_virt last_virt
1865 *
1866 * +-----------------------------------------------+
1867 * | | | physical | | |
1868 * +-----------------------------------------------+
1869 * cur_phys last_phys
1870 *
1871 * 0 w_size - 1
1872 * +-----------------------+
1873 * | | | window |
1874 * +-----------------------+
1875 * cur_window = cur_phys - first_wind
1876 }*/
1877
refresh(register Vi_t * vp,int mode)1878 static void refresh(register Vi_t* vp, int mode)
1879 {
1880 register int p;
1881 register int v;
1882 register int first_w = vp->first_wind;
1883 int p_differ;
1884 int new_lw;
1885 int ncur_phys;
1886 int opflag; /* search optimize flag */
1887
1888 # define w v
1889
1890 /*** find out if it's necessary to start translating at beginning ***/
1891
1892 if(lookahead>0)
1893 {
1894 p = previous[lookahead-1];
1895 if(p != ESC && p != '\n' && p != '\r')
1896 mode = TRANSLATE;
1897 }
1898 v = cur_virt;
1899 #if SHOPT_EDPREDICT
1900 if(mode==INPUT && v>0 && virtual[0]=='#' && v==last_virt && virtual[v]!='*' && sh_isoption(vp->ed->sh,SH_VI))
1901 {
1902 int n;
1903 virtual[last_virt+1] = 0;
1904 # if SHOPT_MULTIBYTE
1905 ed_external(virtual,(char*)virtual);
1906 # endif /* SHOPT_MULTIBYTE */
1907 n = ed_histgen(vp->ed,(char*)virtual);
1908 # if SHOPT_MULTIBYTE
1909 ed_internal((char*)virtual,virtual);
1910 # endif /* SHOPT_MULTIBYTE */
1911 if(vp->ed->hlist)
1912 {
1913 ed_histlist(vp->ed,n);
1914 pr_string(vp,Prompt);
1915 vp->ocur_virt = INVALID;
1916 ed_setcursor(vp->ed,physical,0,cur_phys,0);
1917 }
1918 else
1919 ed_ringbell();
1920 }
1921 else if(mode==INPUT && v<=1 && vp->ed->hlist)
1922 ed_histlist(vp->ed,0);
1923 #endif /* SHOPT_EDPREDICT */
1924 if( v<vp->ocur_virt || vp->ocur_virt==INVALID
1925 || ( v==vp->ocur_virt
1926 && (!is_print(virtual[v]) || !is_print(vp->o_v_char))) )
1927 {
1928 opflag = 0;
1929 p = 0;
1930 v = 0;
1931 }
1932 else
1933 {
1934 opflag = 1;
1935 p = vp->ocur_phys;
1936 v = vp->ocur_virt;
1937 if( !is_print(virtual[v]) )
1938 {
1939 /*** avoid double ^'s ***/
1940 ++p;
1941 ++v;
1942 }
1943 }
1944 virtual[last_virt+1] = 0;
1945 ncur_phys = ed_virt_to_phys(vp->ed,virtual,physical,cur_virt,v,p);
1946 p = genlen(physical);
1947 if( --p < 0 )
1948 last_phys = 0;
1949 else
1950 last_phys = p;
1951
1952 /*** see if this was a translate only ***/
1953
1954 if( mode == TRANSLATE )
1955 return;
1956
1957 /*** adjust left margin if necessary ***/
1958
1959 if( ncur_phys<first_w || ncur_phys>=(first_w + w_size) )
1960 {
1961 cursor(vp,first_w);
1962 first_w = ncur_phys - (w_size>>1);
1963 if( first_w < 0 )
1964 first_w = 0;
1965 vp->first_wind = cur_phys = first_w;
1966 }
1967
1968 /*** attempt to optimize search somewhat to find ***/
1969 /*** out where physical and window images differ ***/
1970
1971 if( first_w==vp->ofirst_wind && ncur_phys>=vp->ocur_phys && opflag==1 )
1972 {
1973 p = vp->ocur_phys;
1974 w = p - first_w;
1975 }
1976 else
1977 {
1978 p = first_w;
1979 w = 0;
1980 }
1981
1982 for(; (p<=last_phys && w<=vp->last_wind); ++p, ++w)
1983 {
1984 if( window[w] != physical[p] )
1985 break;
1986 }
1987 p_differ = p;
1988
1989 if( (p>last_phys || p>=first_w+w_size) && w>vp->last_wind
1990 && cur_virt==vp->ocur_virt )
1991 {
1992 /*** images are identical ***/
1993 return;
1994 }
1995
1996 /*** copy the physical image to the window image ***/
1997
1998 if( last_virt != INVALID )
1999 {
2000 while( p <= last_phys && w < w_size )
2001 window[w++] = physical[p++];
2002 }
2003 new_lw = w;
2004
2005 /*** erase trailing characters if needed ***/
2006
2007 while( w <= vp->last_wind )
2008 window[w++] = ' ';
2009 vp->last_wind = --w;
2010
2011 p = p_differ;
2012
2013 /*** move cursor to start of difference ***/
2014
2015 cursor(vp,p);
2016
2017 /*** and output difference ***/
2018
2019 w = p - first_w;
2020 while( w <= vp->last_wind )
2021 putchar(window[w++]);
2022
2023 cur_phys = w + first_w;
2024 vp->last_wind = --new_lw;
2025
2026 if( last_phys >= w_size )
2027 {
2028 if( first_w == 0 )
2029 vp->long_char = '>';
2030 else if( last_phys < (first_w+w_size) )
2031 vp->long_char = '<';
2032 else
2033 vp->long_char = '*';
2034 }
2035 else
2036 vp->long_char = ' ';
2037
2038 if( vp->long_line != vp->long_char )
2039 {
2040 /*** indicate lines longer than window ***/
2041 while( w++ < w_size )
2042 {
2043 putchar(' ');
2044 ++cur_phys;
2045 }
2046 putchar(vp->long_char);
2047 ++cur_phys;
2048 vp->long_line = vp->long_char;
2049 }
2050
2051 if(vp->ed->e_multiline && vp->ofirst_wind==INVALID && !vp->ed->e_nocrnl)
2052 ed_setcursor(vp->ed, physical, last_phys+1, last_phys+1, -1);
2053 vp->ed->e_nocrnl = 0;
2054 vp->ocur_phys = ncur_phys;
2055 vp->ocur_virt = cur_virt;
2056 vp->ofirst_wind = first_w;
2057
2058 if( mode==INPUT && cur_virt>INVALID )
2059 ++ncur_phys;
2060
2061 cursor(vp,ncur_phys);
2062 ed_flush(vp->ed);
2063 return;
2064 }
2065
2066 /*{ REPLACE( char, increment )
2067 *
2068 * Replace the cur_virt character with char. This routine attempts
2069 * to avoid using refresh().
2070 *
2071 * increment = 1, increment cur_virt after replacement.
2072 * = 0, leave cur_virt where it is.
2073 *
2074 }*/
2075
replace(register Vi_t * vp,register int c,register int increment)2076 static void replace(register Vi_t *vp, register int c, register int increment)
2077 {
2078 register int cur_window;
2079
2080 if( cur_virt == INVALID )
2081 {
2082 /*** can't replace invalid cursor ***/
2083 ed_ringbell();
2084 return;
2085 }
2086 cur_window = cur_phys - vp->first_wind;
2087 if( vp->ocur_virt == INVALID || !is_print(c)
2088 || !is_print(virtual[cur_virt])
2089 || !is_print(vp->o_v_char)
2090 #if SHOPT_MULTIBYTE
2091 || !iswascii(c) || mbwidth(vp->o_v_char)>1
2092 || !iswascii(virtual[cur_virt])
2093 #endif /* SHOPT_MULTIBYTE */
2094 || (increment && (cur_window==w_size-1)
2095 || !is_print(virtual[cur_virt+1])) )
2096 {
2097 /*** must use standard refresh routine ***/
2098
2099 cdelete(vp,1, BAD);
2100 append(vp,c, APPEND);
2101 if( increment && cur_virt<last_virt )
2102 ++cur_virt;
2103 refresh(vp,CONTROL);
2104 }
2105 else
2106 {
2107 virtual[cur_virt] = c;
2108 physical[cur_phys] = c;
2109 window[cur_window] = c;
2110 putchar(c);
2111 if(increment)
2112 {
2113 c = virtual[++cur_virt];
2114 ++cur_phys;
2115 }
2116 else
2117 {
2118 putchar('\b');
2119 }
2120 vp->o_v_char = c;
2121 ed_flush(vp->ed);
2122 }
2123 return;
2124 }
2125
2126 /*{ RESTORE_V()
2127 *
2128 * Restore the contents of virtual space from u_space.
2129 *
2130 }*/
2131
restore_v(register Vi_t * vp)2132 static void restore_v(register Vi_t *vp)
2133 {
2134 register int tmpcol;
2135 genchar tmpspace[MAXLINE];
2136
2137 if( vp->u_column == INVALID-1 )
2138 {
2139 /*** never saved anything ***/
2140 ed_ringbell();
2141 return;
2142 }
2143 gencpy(tmpspace, vp->u_space);
2144 tmpcol = vp->u_column;
2145 save_v(vp);
2146 gencpy(virtual, tmpspace);
2147 cur_virt = tmpcol;
2148 last_virt = genlen(tmpspace) - 1;
2149 vp->ocur_virt = MAXCHAR; /** invalidate refresh optimization **/
2150 return;
2151 }
2152
2153 /*{ SAVE_LAST()
2154 *
2155 * If the user has typed something, save it in last line.
2156 *
2157 }*/
2158
save_last(register Vi_t * vp)2159 static void save_last(register Vi_t* vp)
2160 {
2161 register int i;
2162
2163 if( (i = cur_virt - first_virt + 1) > 0 )
2164 {
2165 /*** save last thing user typed ***/
2166 if(i >= MAXLINE)
2167 i = MAXLINE-1;
2168 genncpy(vp->lastline, (&virtual[first_virt]), i);
2169 vp->lastline[i] = '\0';
2170 }
2171 return;
2172 }
2173
2174 /*{ SAVE_V()
2175 *
2176 * This routine will save the contents of virtual in u_space.
2177 *
2178 }*/
2179
save_v(register Vi_t * vp)2180 static void save_v(register Vi_t *vp)
2181 {
2182 if(!inmacro)
2183 {
2184 virtual[last_virt + 1] = '\0';
2185 gencpy(vp->u_space, virtual);
2186 vp->u_column = cur_virt;
2187 }
2188 return;
2189 }
2190
2191 /*{ SEARCH( mode )
2192 *
2193 * Search history file for regular expression.
2194 *
2195 * mode = '/' require search string and search new to old
2196 * mode = '?' require search string and search old to new
2197 * mode = 'N' repeat last search in reverse direction
2198 * mode = 'n' repeat last search
2199 *
2200 }*/
2201
2202 /*
2203 * search for <string> in the current command
2204 */
curline_search(Vi_t * vp,const char * string)2205 static int curline_search(Vi_t *vp, const char *string)
2206 {
2207 register size_t len=strlen(string);
2208 register const char *dp,*cp=string, *dpmax;
2209 #if SHOPT_MULTIBYTE
2210 ed_external(vp->u_space,(char*)vp->u_space);
2211 #endif /* SHOPT_MULTIBYTE */
2212 for(dp=(char*)vp->u_space,dpmax=dp+strlen(dp)-len; dp<=dpmax; dp++)
2213 {
2214 if(*dp==*cp && memcmp(cp,dp,len)==0)
2215 return(dp-(char*)vp->u_space);
2216 }
2217 #if SHOPT_MULTIBYTE
2218 ed_internal((char*)vp->u_space,vp->u_space);
2219 #endif /* SHOPT_MULTIBYTE */
2220 return(-1);
2221 }
2222
search(register Vi_t * vp,register int mode)2223 static int search(register Vi_t* vp,register int mode)
2224 {
2225 register int new_direction;
2226 register int oldcurhline;
2227 register int i;
2228 Histloc_t location;
2229
2230 if( vp->direction == -2 && mode != 'n')
2231 vp->direction = -1;
2232 if( mode == '/' || mode == '?')
2233 {
2234 /*** new search expression ***/
2235 del_line(vp,BAD);
2236 append(vp,mode, APPEND);
2237 refresh(vp,INPUT);
2238 first_virt = 1;
2239 vigetline(vp,SEARCH);
2240 first_virt = 0;
2241 virtual[last_virt + 1] = '\0'; /*** make null terminated ***/
2242 vp->direction = mode=='/' ? -1 : 1;
2243 }
2244
2245 if( cur_virt == INVALID )
2246 {
2247 /*** no operation ***/
2248 return(ABORT);
2249 }
2250
2251 if( cur_virt==0 || fold(mode)=='N' )
2252 {
2253 /*** user wants repeat of last search ***/
2254 del_line(vp,BAD);
2255 strcpy( ((char*)virtual)+1, lsearch);
2256 #if SHOPT_MULTIBYTE
2257 *((char*)virtual) = '/';
2258 ed_internal((char*)virtual,virtual);
2259 #endif /* SHOPT_MULTIBYTE */
2260 }
2261
2262 if( mode == 'N' )
2263 new_direction = -vp->direction;
2264 else
2265 new_direction = vp->direction;
2266
2267
2268 /*** now search ***/
2269
2270 oldcurhline = curhline;
2271 #if SHOPT_MULTIBYTE
2272 ed_external(virtual,(char*)virtual);
2273 #endif /* SHOPT_MULTIBYTE */
2274 if(mode=='?' && (i=curline_search(vp,((char*)virtual)+1))>=0)
2275 {
2276 location.hist_command = curhline;
2277 location.hist_char = i;
2278 }
2279 else
2280 {
2281 i = INVALID;
2282 if( new_direction==1 && curhline >= histmax )
2283 curhline = histmin + 1;
2284 location = hist_find(shgd->hist_ptr,((char*)virtual)+1, curhline, 1, new_direction);
2285 }
2286 cur_virt = i;
2287 strncpy(lsearch, ((char*)virtual)+1, SEARCHSIZE);
2288 lsearch[SEARCHSIZE-1] = 0;
2289 if( (curhline=location.hist_command) >=0 )
2290 {
2291 vp->ocur_virt = INVALID;
2292 return(GOOD);
2293 }
2294
2295 /*** could not find matching line ***/
2296
2297 curhline = oldcurhline;
2298 return(BAD);
2299 }
2300
2301 /*{ SYNC_CURSOR()
2302 *
2303 * This routine will move the physical cursor to the same
2304 * column as the virtual cursor.
2305 *
2306 }*/
2307
sync_cursor(register Vi_t * vp)2308 static void sync_cursor(register Vi_t *vp)
2309 {
2310 register int p;
2311 register int v;
2312 register int c;
2313 int new_phys;
2314
2315 if( cur_virt == INVALID )
2316 return;
2317
2318 /*** find physical col that corresponds to virtual col ***/
2319
2320 new_phys = 0;
2321 if(vp->first_wind==vp->ofirst_wind && cur_virt>vp->ocur_virt && vp->ocur_virt!=INVALID)
2322 {
2323 /*** try to optimize search a little ***/
2324 p = vp->ocur_phys + 1;
2325 #if SHOPT_MULTIBYTE
2326 while(physical[p]==MARKER)
2327 p++;
2328 #endif /* SHOPT_MULTIBYTE */
2329 v = vp->ocur_virt + 1;
2330 }
2331 else
2332 {
2333 p = 0;
2334 v = 0;
2335 }
2336 for(; v <= last_virt; ++p, ++v)
2337 {
2338 #if SHOPT_MULTIBYTE
2339 int d;
2340 c = virtual[v];
2341 if((d = mbwidth(c)) > 1)
2342 {
2343 if( v != cur_virt )
2344 p += (d-1);
2345 }
2346 else if(!iswprint(c))
2347 #else
2348 c = virtual[v];
2349 if(!isprint(c))
2350 #endif /* SHOPT_MULTIBYTE */
2351 {
2352 if( c == '\t' )
2353 {
2354 p -= ((p+editb.e_plen)%TABSIZE);
2355 p += (TABSIZE-1);
2356 }
2357 else
2358 {
2359 ++p;
2360 }
2361 }
2362 if( v == cur_virt )
2363 {
2364 new_phys = p;
2365 break;
2366 }
2367 }
2368
2369 if( new_phys < vp->first_wind || new_phys >= vp->first_wind + w_size )
2370 {
2371 /*** asked to move outside of window ***/
2372
2373 window[0] = '\0';
2374 refresh(vp,CONTROL);
2375 return;
2376 }
2377
2378 cursor(vp,new_phys);
2379 ed_flush(vp->ed);
2380 vp->ocur_phys = cur_phys;
2381 vp->ocur_virt = cur_virt;
2382 vp->o_v_char = virtual[vp->ocur_virt];
2383
2384 return;
2385 }
2386
2387 /*{ TEXTMOD( command, mode )
2388 *
2389 * Modify text operations.
2390 *
2391 * mode != 0, repeat previous operation
2392 *
2393 }*/
2394
textmod(register Vi_t * vp,register int c,int mode)2395 static int textmod(register Vi_t *vp,register int c, int mode)
2396 {
2397 register int i;
2398 register genchar *p = vp->lastline;
2399 register int trepeat = vp->repeat;
2400 genchar *savep;
2401 int ch;
2402
2403 if(mode && (fold(vp->lastmotion)=='F' || fold(vp->lastmotion)=='T'))
2404 vp->lastmotion = ';';
2405
2406 if( fold(c) == 'P' )
2407 {
2408 /*** change p from lastline to yankbuf ***/
2409 p = yankbuf;
2410 }
2411
2412 addin:
2413 switch( c )
2414 {
2415 /***** Input commands *****/
2416
2417 #if KSHELL
2418 case '\t':
2419 if(vp->ed->e_tabcount!=1)
2420 return(BAD);
2421 c = '=';
2422 case '*': /** do file name expansion in place **/
2423 case '\\': /** do file name completion in place **/
2424 if( cur_virt == INVALID )
2425 return(BAD);
2426 case '=': /** list file name expansions **/
2427 save_v(vp);
2428 i = last_virt;
2429 ++last_virt;
2430 mode = cur_virt-1;
2431 virtual[last_virt] = 0;
2432 ch = c;
2433 if(mode>=0 && c=='\\' && virtual[mode+1]=='/')
2434 c = '=';
2435 if(ed_expand(vp->ed,(char*)virtual, &cur_virt, &last_virt, ch, vp->repeat_set?vp->repeat:-1)<0)
2436 {
2437 if(vp->ed->e_tabcount)
2438 {
2439 vp->ed->e_tabcount=2;
2440 ed_ungetchar(vp->ed,'\t');
2441 --last_virt;
2442 return(APPEND);
2443 }
2444 last_virt = i;
2445 ed_ringbell();
2446 }
2447 else if((c=='=' || (c=='\\'&&virtual[last_virt]=='/')) && !vp->repeat_set)
2448 {
2449 vp->nonewline++;
2450 ed_ungetchar(vp->ed,cntl('L'));
2451 return(GOOD);
2452 }
2453 else
2454 {
2455 --cur_virt;
2456 --last_virt;
2457 vp->ocur_virt = MAXCHAR;
2458 if(c=='=' || (mode<cur_virt && virtual[cur_virt]=='/'))
2459 vp->ed->e_tabcount = 0;
2460 return(APPEND);
2461 }
2462 break;
2463
2464 case '@': /** macro expansion **/
2465 if( mode )
2466 c = vp->lastmacro;
2467 else
2468 if((c=getrchar(vp))==ESC)
2469 return(GOOD);
2470 if(!inmacro)
2471 vp->lastmacro = c;
2472 if(ed_macro(vp->ed,c))
2473 {
2474 save_v(vp);
2475 inmacro++;
2476 return(GOOD);
2477 }
2478 ed_ringbell();
2479 return(BAD);
2480
2481 #endif /* KSHELL */
2482 case '_': /** append last argument of prev command **/
2483 save_v(vp);
2484 {
2485 genchar tmpbuf[MAXLINE];
2486 if(vp->repeat_set==0)
2487 vp->repeat = -1;
2488 p = (genchar*)hist_word((char*)tmpbuf,MAXLINE,vp->repeat);
2489 if(p==0)
2490 {
2491 ed_ringbell();
2492 break;
2493 }
2494 #if SHOPT_MULTIBYTE
2495 ed_internal((char*)p,tmpbuf);
2496 p = tmpbuf;
2497 #endif /* SHOPT_MULTIBYTE */
2498 i = ' ';
2499 do
2500 {
2501 append(vp,i,APPEND);
2502 }
2503 while(i = *p++);
2504 return(APPEND);
2505 }
2506
2507 case 'A': /** append to end of line **/
2508 cur_virt = last_virt;
2509 sync_cursor(vp);
2510
2511 case 'a': /** append **/
2512 if( fold(mode) == 'A' )
2513 {
2514 c = 'p';
2515 goto addin;
2516 }
2517 save_v(vp);
2518 if( cur_virt != INVALID )
2519 {
2520 first_virt = cur_virt + 1;
2521 cursor(vp,cur_phys + 1);
2522 ed_flush(vp->ed);
2523 }
2524 return(APPEND);
2525
2526 case 'I': /** insert at beginning of line **/
2527 cur_virt = first_virt;
2528 sync_cursor(vp);
2529
2530 case 'i': /** insert **/
2531 if( fold(mode) == 'I' )
2532 {
2533 c = 'P';
2534 goto addin;
2535 }
2536 save_v(vp);
2537 if( cur_virt != INVALID )
2538 {
2539 vp->o_v_char = virtual[cur_virt];
2540 first_virt = cur_virt--;
2541 }
2542 return(INSERT);
2543
2544 case 'C': /** change to eol **/
2545 c = '$';
2546 goto chgeol;
2547
2548 case 'c': /** change **/
2549 if( mode )
2550 c = vp->lastmotion;
2551 else
2552 c = getcount(vp,ed_getchar(vp->ed,-1));
2553 chgeol:
2554 vp->lastmotion = c;
2555 if( c == 'c' )
2556 {
2557 del_line(vp,GOOD);
2558 return(APPEND);
2559 }
2560
2561 if(!delmotion(vp, c, 'c'))
2562 return(BAD);
2563
2564 if( mode == 'c' )
2565 {
2566 c = 'p';
2567 trepeat = 1;
2568 goto addin;
2569 }
2570 first_virt = cur_virt + 1;
2571 return(APPEND);
2572
2573 case 'D': /** delete to eol **/
2574 c = '$';
2575 goto deleol;
2576
2577 case 'd': /** delete **/
2578 if( mode )
2579 c = vp->lastmotion;
2580 else
2581 c = getcount(vp,ed_getchar(vp->ed,-1));
2582 deleol:
2583 vp->lastmotion = c;
2584 if( c == 'd' )
2585 {
2586 del_line(vp,GOOD);
2587 break;
2588 }
2589 if(!delmotion(vp, c, 'd'))
2590 return(BAD);
2591 if( cur_virt < last_virt )
2592 ++cur_virt;
2593 break;
2594
2595 case 'P':
2596 if( p[0] == '\0' )
2597 return(BAD);
2598 if( cur_virt != INVALID )
2599 {
2600 i = virtual[cur_virt];
2601 if(!is_print(i))
2602 vp->ocur_virt = INVALID;
2603 --cur_virt;
2604 }
2605
2606 case 'p': /** print **/
2607 if( p[0] == '\0' )
2608 return(BAD);
2609
2610 if( mode != 's' && mode != 'c' )
2611 {
2612 save_v(vp);
2613 if( c == 'P' )
2614 {
2615 /*** fix stored cur_virt ***/
2616 ++vp->u_column;
2617 }
2618 }
2619 if( mode == 'R' )
2620 mode = REPLACE;
2621 else
2622 mode = APPEND;
2623 savep = p;
2624 for(i=0; i<trepeat; ++i)
2625 {
2626 while(c= *p++)
2627 append(vp,c,mode);
2628 p = savep;
2629 }
2630 break;
2631
2632 case 'R': /* Replace many chars **/
2633 if( mode == 'R' )
2634 {
2635 c = 'P';
2636 goto addin;
2637 }
2638 save_v(vp);
2639 if( cur_virt != INVALID )
2640 first_virt = cur_virt;
2641 return(REPLACE);
2642
2643 case 'r': /** replace **/
2644 if( mode )
2645 c = *p;
2646 else
2647 if((c=getrchar(vp))==ESC)
2648 return(GOOD);
2649 *p = c;
2650 save_v(vp);
2651 while(trepeat--)
2652 replace(vp,c, trepeat!=0);
2653 return(GOOD);
2654
2655 case 'S': /** Substitute line - cc **/
2656 c = 'c';
2657 goto chgeol;
2658
2659 case 's': /** substitute **/
2660 save_v(vp);
2661 cdelete(vp,vp->repeat, BAD);
2662 if( mode )
2663 {
2664 c = 'p';
2665 trepeat = 1;
2666 goto addin;
2667 }
2668 first_virt = cur_virt + 1;
2669 return(APPEND);
2670
2671 case 'Y': /** Yank to end of line **/
2672 c = '$';
2673 goto yankeol;
2674
2675 case 'y': /** yank thru motion **/
2676 if( mode )
2677 c = vp->lastmotion;
2678 else
2679 c = getcount(vp,ed_getchar(vp->ed,-1));
2680 yankeol:
2681 vp->lastmotion = c;
2682 if( c == 'y' )
2683 {
2684 gencpy(yankbuf, virtual);
2685 }
2686 else if(!delmotion(vp, c, 'y'))
2687 {
2688 return(BAD);
2689 }
2690 break;
2691
2692 case 'x': /** delete repeat chars forward - dl **/
2693 c = 'l';
2694 goto deleol;
2695
2696 case 'X': /** delete repeat chars backward - dh **/
2697 c = 'h';
2698 goto deleol;
2699
2700 case '~': /** invert case and advance **/
2701 if( cur_virt != INVALID )
2702 {
2703 save_v(vp);
2704 i = INVALID;
2705 while(trepeat-->0 && i!=cur_virt)
2706 {
2707 i = cur_virt;
2708 c = virtual[cur_virt];
2709 #if SHOPT_MULTIBYTE
2710 if((c&~STRIP)==0)
2711 #endif /* SHOPT_MULTIBYTE */
2712 if( isupper(c) )
2713 c = tolower(c);
2714 else if( islower(c) )
2715 c = toupper(c);
2716 replace(vp,c, 1);
2717 }
2718 return(GOOD);
2719 }
2720 else
2721 return(BAD);
2722
2723 default:
2724 return(BAD);
2725 }
2726 refresh(vp,CONTROL);
2727 return(GOOD);
2728 }
2729
2730
2731 #if SHOPT_MULTIBYTE
_isalph(register int v)2732 static int _isalph(register int v)
2733 {
2734 #ifdef _lib_iswalnum
2735 return(iswalnum(v) || v=='_');
2736 #else
2737 return((v&~STRIP) || isalnum(v) || v=='_');
2738 #endif
2739 }
2740
2741
_isblank(register int v)2742 static int _isblank(register int v)
2743 {
2744 return((v&~STRIP)==0 && isspace(v));
2745 }
2746
_ismetach(register int v)2747 static int _ismetach(register int v)
2748 {
2749 return((v&~STRIP)==0 && ismeta(v));
2750 }
2751
2752 #endif /* SHOPT_MULTIBYTE */
2753
2754 /*
2755 * get a character, after ^V processing
2756 */
getrchar(register Vi_t * vp)2757 static int getrchar(register Vi_t *vp)
2758 {
2759 register int c;
2760 if((c=ed_getchar(vp->ed,1))== usrlnext)
2761 c = ed_getchar(vp->ed,2);
2762 return(c);
2763 }
2764