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