1 /***********************************************************************
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 *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * David Korn <dgk@research.att.com> *
19 ***********************************************************************/
21 /* Adapted for ksh by David Korn */
22 /*+ VI.C P.D. Sullivan
24 * One line editor for the shell based on the vi editor.
36 # include "FEATURE/options"
44 #include "FEATURE/time"
48 # define echoctl (vp->ed->e_echoctl)
51 # define echoctl ECHOCTL
55 #endif /*SHOPT_OLDTERMIO */
58 # define NTICKS 5 /* number of ticks for typeahead */
61 #define MAXCHAR MAXLINE-2 /* max char per line */
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))
73 static int _isalph(int);
74 static int _ismetach(int);
75 static int _isblank(int);
77 # define isblank(v) _isblank(virtual[v])
78 # define isalph(v) _isalph(virtual[v])
79 # define ismetach(v) _ismetach(virtual[v])
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))
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 */
93 #if ( 'a' == 97) /* ASCII? */
94 # define fold(c) ((c)&~040) /* lower and uppercase equivalent */
96 # define fold(c) ((c)|0100) /* lower and uppercase equivalent */
100 #define iswascii(c) (!((c)&(~0177)))
107 char addnl
; /* boolean - add newline flag */
108 char last_find
; /* last find command */
109 char last_cmd
; /* last command */
112 int findchar
; /* last find char */
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 */
130 clock_t typeahead
; /* typeahead occurred */
132 int typeahead
; /* typeahead occurred */
133 #endif /* FIORDCHK */
137 Edit_t
*ed
; /* pointer to edit data */
140 #define editb (*vp->ed)
143 #define putchar(c) ed_putchar(vp->ed,c)
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 */
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 */
186 #define INVALID (-1) /* invalid column */
188 static const char paren_chars
[] = "([{)]}"; /* for % command */
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);
207 /*+ VI_READ( fd, shbuf, nchar )
209 * This routine implements a one line version of vi and is
210 * called by _filbuf.c
215 * if reedit is non-zero, initialize edit buffer with reedit chars
217 int ed_viread(void *context
, int fd
, register char *shbuf
, int nchar
, int reedit
)
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 */
234 int viraw
= (sh_isoption(SH_VIRAW
) || sh
.st
.trap
[SH_KEYTRAP
]);
236 clock_t oldtime
, newtime
;
238 # endif /* FIORDCHK */
239 #endif /* SHOPT_RAWONLY */
242 ed
->e_vi
= vp
= newof(0,Vi_t
,1,0);
243 vp
->lastline
= (genchar
*)malloc(MAXLINE
*CHARSIZE
);
248 /*** setup prompt ***/
251 ed_setup(vp
->ed
,fd
, reedit
);
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));
263 ioctl(fd
,FIORDCHK
,&vp
->typeahead
);
265 /* time the current line to determine typeahead */
266 oldtime
= times(&dummy
);
267 #endif /* FIORDCHK */
269 /* abort of interrupt has occurred */
270 if(sh
.trapnote
&SH_SIGSET
)
274 /*** Read the line ***/
275 i
= ed_read(context
, fd
, shbuf
, nchar
, 0);
277 newtime
= times(&dummy
);
278 vp
->typeahead
= ((newtime
-oldtime
) < NTICKS
);
279 #endif /* FIORDCHK */
284 /*** read error or eof typed ***/
288 term_char
= shbuf
[--i
];
289 if( term_char
== '\r' )
291 if( term_char
=='\n' || term_char
==ESC
)
298 register int c
= shbuf
[0];
300 /*** Save and remove the last character if its an eol, ***/
301 /* changing '\r' to '\n' */
305 /*** ESC was typed as first char of line ***/
308 shbuf
[i
--] = '\0'; /* null terminate line */
310 else if( i
<0 || c
==usreof
)
312 /*** read error or eof typed ***/
320 term_char
= shbuf
[--i
];
321 if( term_char
== '\r' )
323 #if !defined(VEOL2) && !defined(ECHOCTL)
330 if( term_char
=='\n' || term_char
==usreof
)
332 /*** remove terminator & null terminate ***/
337 /** terminator was ESC, which is not xmitted **/
345 #endif /* SHOPT_RAWONLY */
347 /*** Set raw mode ***/
350 if( editb
.e_ttyspeed
== 0 )
352 /*** never did TCGETA, so do it ***/
353 /* avoids problem if user does 'sh -o viraw' */
356 #endif /* SHOPT_RAWONLY */
357 if(tty_raw(ERRIO
,0) < 0 )
358 return(reedit
?reedit
:ed_read(context
, fd
, shbuf
, nchar
,0));
362 /*** Initialize some things ***/
364 virtual = (genchar
*)shbuf
;
366 virtual = (genchar
*)roundof((char*)virtual-(char*)0,sizeof(genchar
));
368 i
= ed_internal(shbuf
,virtual)-1;
369 #endif /* SHOPT_MULTIBYTE */
382 vp
->ocur_virt
= MAXCHAR
;
385 vp
->u_column
= INVALID
- 1;
392 yankbuf
= (genchar
*)malloc(MAXLINE
*CHARSIZE
);
393 if( vp
->last_cmd
== '\0' )
395 /*** first time for this shell ***/
398 vp
->findchar
= INVALID
;
399 vp
->lastmotion
= '\0';
405 /*** fiddle around with prompt length ***/
406 if( nchar
+plen
> MAXCHAR
)
407 nchar
= MAXCHAR
- plen
;
413 for(i
=(echoctl
?last_virt
:0); i
<last_virt
; ++i
)
415 /*** change \r to \n, check for control characters, ***/
416 /* delete appropriate ^Vs, */
417 /* and estimate last physical column */
419 if( virtual[i
] == '\r' )
423 register int c
= virtual[i
];
426 /*** user typed escaped erase or kill char ***/
431 else if( !is_print(c
) )
439 /*** eol/eof was escaped ***/
440 /* so replace ^V with it */
441 virtual[i
] = term_char
;
446 gencpy((&virtual[i
]), (&virtual[i
+1]));
454 /*** copy virtual image to window ***/
456 last_phys
= ed_virt_to_phys(vp
->ed
,virtual,physical
,last_virt
,0,0);
457 if( last_phys
>= w_size
)
459 /*** line longer than window ***/
460 vp
->last_wind
= w_size
- 1;
463 vp
->last_wind
= last_phys
;
464 genncpy(window
, virtual, vp
->last_wind
+1);
466 if( term_char
!=ESC
&& (last_virt
==INVALID
467 || virtual[last_virt
]!=term_char
) )
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) ) */
477 if( editb
.e_ttyspeed
==FAST
&& last_virt
!=INVALID
478 && (vp
->typeahead
|| cntl_char
) )
480 refresh(vp
,TRANSLATE
);
481 pr_string(vp
,Prompt
);
482 putstring(vp
,0, last_phys
+1);
486 while(kill_erase
-- > 0)
490 if( term_char
=='\n' )
494 virtual[++last_virt
] = '\n';
499 virtual[last_virt
+1] = 0;
500 last_virt
= ed_external(virtual,shbuf
);
504 #endif /* SHOPT_MULTIBYTE */
507 /*** Line terminated with escape, or escaped eol/eof, ***/
508 /* so set raw mode */
510 if( tty_raw(ERRIO
,0) < 0 )
514 * The following prevents drivers that return 0 on
515 * causing an infinite loop
519 virtual[++last_virt
] = '\n';
521 virtual[last_virt
+1] = 0;
522 last_virt
= ed_external(virtual,shbuf
);
526 #endif /* SHOPT_MULTIBYTE */
529 if(echoctl
) /*** for cntl-echo erase the ^[ ***/
530 pr_string(vp
,"\b\b\b\b \b\b");
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 ***/
541 pr_string(vp
,Prompt
);
542 if( term_char
==ESC
&& (last_virt
<0 || virtual[last_virt
]!=ESC
))
549 /*** just update everything internally ***/
550 refresh(vp
,TRANSLATE
);
554 /*** Handle usrintr, usrquit, or EOF ***/
556 i
= sigsetjmp(editb
.e_env
,0);
559 if(vp
->ed
->e_multiline
)
561 cur_virt
= last_virt
;
580 /*** Get a line from the terminal ***/
585 cur_phys
= vp
->first_wind
;
586 vp
->ofirst_wind
= INVALID
;
591 else if(last_virt
>=0 && virtual[last_virt
]==term_char
)
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 */
603 stakset(ed
->e_stkptr
,ed
->e_stkoff
);
607 virtual[++last_virt
] = '\n';
610 if( ++last_virt
>= 0 )
616 shbuf
[last_virt
-1] = '\n';
620 virtual[last_virt
] = 0;
621 last_virt
= ed_external(virtual,shbuf
);
623 #endif /* SHOPT_MULTIBYTE */
631 /*{ APPEND( char, mode )
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
639 static void append(Vi_t
*vp
,int c
, int mode
)
643 if( last_virt
<max_col
&& last_phys
<max_col
)
645 if( mode
==APPEND
|| (cur_virt
==last_virt
&& last_virt
>=0))
647 j
= (cur_virt
>=0?cur_virt
:0);
648 for(i
= ++last_virt
; i
> j
; --i
)
649 virtual[i
] = virtual[i
-1];
651 virtual[++cur_virt
] = c
;
658 /*{ BACKWORD( nwords, cmd )
660 * This routine will position cur_virt at the nth previous word.
664 static void backword(Vi_t
*vp
,int nwords
, register int cmd
)
666 register int tcur_virt
= cur_virt
;
667 while( nwords
-- && tcur_virt
> first_virt
)
669 if( !isblank(tcur_virt
) && isblank(tcur_virt
-1)
670 && tcur_virt
>first_virt
)
674 register int last
= isalph(tcur_virt
-1);
675 register int cur
= isalph(tcur_virt
);
676 if((!cur
&& last
) || (cur
&& !last
))
679 while( isblank(tcur_virt
) && tcur_virt
>=first_virt
)
683 while( !isblank(tcur_virt
) && tcur_virt
>=first_virt
)
688 if(isalph(tcur_virt
))
689 while( isalph(tcur_virt
) && tcur_virt
>=first_virt
)
692 while( !isalph(tcur_virt
) && !isblank(tcur_virt
)
693 && tcur_virt
>=first_virt
)
696 cur_virt
= ++tcur_virt
;
703 * This routine implements the vi command subset.
704 * The cursor will always be positioned at the char of interest.
708 static int cntlmode(Vi_t
*vp
)
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 */
719 /*** save virtual image if never done before ***/
720 virtual[last_virt
+1] = '\0';
721 gencpy(vp
->U_space
, virtual);
727 real_u_space
= vp
->u_space
;
731 if( cur_virt
> INVALID
)
733 /*** make sure cursor is at the last char ***/
737 /*** Read control char until something happens to cause a ***/
738 /* return to APPEND/REPLACE mode */
740 while( c
=ed_getchar(vp
->ed
,-1) )
743 was_inmacro
= inmacro
;
746 /*** move to leftmost column ***/
756 vp
->lastrepeat
= vp
->repeat
;
759 /*** see if it's a move cursor command ***/
768 /*** see if it's a repeat of the last command ***/
773 vp
->repeat
= vp
->lastrepeat
;
774 i
= textmod(vp
,c
, c
);
778 i
= textmod(vp
,c
, 0);
781 /*** see if it's a text modification command ***/
788 default: /** input mode **/
792 vp
->lastrepeat
= vp
->repeat
;
802 /***** Other stuff *****/
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
)
810 pr_string(vp
,Prompt
);
812 cur_phys
= vp
->first_wind
;
813 vp
->ofirst_wind
= INVALID
;
819 register const char *p
= fmtident(e_version
);
825 ed_getchar(vp
->ed
,-1);
830 case '/': /** Search **/
835 switch( search(vp
,c
) )
838 /*** force a total refresh ***/
847 if( vp
->u_column
== INVALID
)
855 case 'j': /** get next command **/
856 case '+': /** get next command **/
857 curhline
+= vp
->repeat
;
858 if( curhline
> histmax
)
863 else if(curhline
==histmax
&& tmp_u_column
!=INVALID
)
865 vp
->u_space
= tmp_u_space
;
866 vp
->u_column
= tmp_u_column
;
868 vp
->u_space
= real_u_space
;
875 case 'k': /** get previous command **/
876 case '-': /** get previous command **/
877 if( curhline
== histmax
)
879 vp
->u_space
= tmp_u_space
;
882 vp
->u_space
= real_u_space
;
883 tmp_u_column
= vp
->u_column
;
887 curhline
-= vp
->repeat
;
888 if( curhline
<= histmin
)
890 curhline
+= vp
->repeat
;
896 if(curhline
!=histmax
|| cur_virt
==INVALID
)
897 hist_copy((char*)virtual, MAXLINE
, curhline
,-1);
900 strcpy((char*)virtual,(char*)vp
->u_space
);
902 ed_internal((char*)vp
->u_space
,vp
->u_space
);
903 #endif /* SHOPT_MULTIBYTE */
906 ed_internal((char*)virtual,virtual);
907 #endif /* SHOPT_MULTIBYTE */
908 if((last_virt
=genlen(virtual)-1) >= 0 && cur_virt
== INVALID
)
913 case 'u': /** undo the last thing done **/
917 case 'U': /** Undo everything **/
919 if( virtual[0] == '\0' )
923 gencpy(virtual, vp
->U_space
);
924 last_virt
= genlen(vp
->U_space
) - 1;
931 if(vp
->repeat_set
==0)
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
)
942 curhline
= vp
->repeat
;
952 if(ed_fulledit(vp
->ed
)==GOOD
)
958 case '#': /** insert(delete) # to (no)comment command **/
959 if( cur_virt
!= INVALID
)
961 register genchar
*p
= &virtual[last_virt
+1];
963 /*** see whether first char is comment char ***/
964 c
= (virtual[0]=='#');
965 while(p
-- >= virtual)
967 if(*p
=='\n' || p
<virtual)
969 if(c
) /* delete '#' */
979 cur_virt
= p
-virtual;
980 append(vp
,'#', APPEND
);
993 case '\n': /** send to shell **/
997 /* don't ring bell if next char is '[' */
1001 if(sfpkrd(editb
.e_fd
,&x
,1,'\r',400L,-1)>0)
1002 ed_ungetchar(vp
->ed
,x
);
1006 ed_ungetchar(vp
->ed
,c
=ed_getchar(vp
->ed
,1));
1020 refresh(vp
,CONTROL
);
1027 /*{ CURSOR( new_current_physical )
1029 * This routine will position the virtual cursor at
1030 * physical column x in the window.
1034 static void cursor(Vi_t
*vp
,register int x
)
1037 while(physical
[x
]==MARKER
)
1039 #endif /* SHOPT_MULTIBYTE */
1040 cur_phys
= ed_setcursor(vp
->ed
, physical
, cur_phys
,x
,vp
->first_wind
);
1043 /*{ DELETE( nchars, mode )
1045 * Delete nchars from the virtual space and leave cur_virt positioned
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.
1054 static void cdelete(Vi_t
*vp
,register int nchars
, int mode
)
1057 register genchar
*cp
;
1059 if( cur_virt
< first_virt
)
1066 cp
= virtual+cur_virt
;
1067 vp
->o_v_char
= cp
[0];
1068 if( (cur_virt
-- + nchars
) > last_virt
)
1070 /*** set nchars to number actually deleted ***/
1071 nchars
= last_virt
- cur_virt
;
1074 /*** save characters to be deleted ***/
1084 /*** now delete these characters ***/
1088 gencpy(cp
,cp
+nchars
);
1089 last_virt
-= nchars
;
1095 /*{ DEL_LINE( mode )
1097 * This routine will delete the line.
1098 * mode = GOOD, do a save_v()
1101 static void del_line(register Vi_t
*vp
, int mode
)
1103 if( last_virt
== INVALID
)
1111 cdelete(vp
,last_virt
+1, BAD
);
1112 refresh(vp
,CONTROL
);
1116 vp
->findchar
= INVALID
;
1117 last_phys
= INVALID
;
1118 last_virt
= INVALID
;
1119 vp
->last_wind
= INVALID
;
1121 vp
->o_v_char
= '\0';
1123 vp
->ocur_virt
= MAXCHAR
;
1124 vp
->ofirst_wind
= 0;
1129 /*{ DELMOTION( motion, mode )
1131 * Delete thru motion.
1133 * mode = 'd', save deleted characters, delete
1134 * = 'c', do not save characters, change
1135 * = 'y', save characters, yank
1137 * Returns 1 if operation successful; else 0.
1141 static int delmotion(Vi_t
*vp
,int motion
, int mode
)
1143 register int begin
, end
, delta
;
1144 /* the following saves a register */
1146 if( cur_virt
== INVALID
)
1152 /*** fake out the motion routines by appending a blank ***/
1154 virtual[++last_virt
] = ' ';
1155 end
= mvcursor(vp
,motion
);
1156 virtual[last_virt
--] = 0;
1161 if( mode
=='c' && end
>begin
&& strchr("wW", motion
) )
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) )
1171 delta
= end
- begin
;
1175 if( strchr("eE;,TtFf%", motion
) )
1180 delta
= -delta
+ (motion
=='%');
1183 cdelete(vp
,delta
, mode
);
1190 /*{ ENDWORD( nwords, cmd )
1192 * This routine will move cur_virt to the end of the nth word.
1196 static void endword(Vi_t
*vp
, int nwords
, register int cmd
)
1198 register int tcur_virt
= cur_virt
;
1201 if( !isblank(tcur_virt
) && tcur_virt
<=last_virt
)
1203 while( isblank(tcur_virt
) && tcur_virt
<=last_virt
)
1207 while( !isblank(tcur_virt
) && tcur_virt
<=last_virt
)
1212 if( isalph(tcur_virt
) )
1213 while( isalph(tcur_virt
) && tcur_virt
<=last_virt
)
1216 while( !isalph(tcur_virt
) && !isblank(tcur_virt
)
1217 && tcur_virt
<=last_virt
)
1220 if( tcur_virt
> first_virt
)
1223 cur_virt
= tcur_virt
;
1227 /*{ FORWARD( nwords, cmd )
1229 * This routine will move cur_virt forward to the next nth word.
1233 static void forward(Vi_t
*vp
,register int nwords
, int cmd
)
1235 register int tcur_virt
= cur_virt
;
1240 while( !isblank(tcur_virt
) && tcur_virt
< last_virt
)
1245 if( isalph(tcur_virt
) )
1247 while( isalph(tcur_virt
) && tcur_virt
<last_virt
)
1252 while( !isalph(tcur_virt
) && !isblank(tcur_virt
)
1253 && tcur_virt
< last_virt
)
1257 while( isblank(tcur_virt
) && tcur_virt
< last_virt
)
1260 cur_virt
= tcur_virt
;
1268 * Set repeat to the user typed number and return the terminating
1273 static int getcount(register Vi_t
*vp
,register int c
)
1277 /*** get any repeat count ***/
1287 c
= ed_getchar(vp
->ed
,-1);
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
1306 * The cursor will always be positioned after the last
1309 * This routine returns when cr, nl, or (eof in column 0) is
1310 * received (column 0 is the first char position).
1314 static void getline(register Vi_t
* vp
,register int mode
)
1318 int max_virt
=0, last_save
=0;
1319 genchar saveline
[MAXLINE
];
1325 /*** go directly to control mode ***/
1331 if( (c
=ed_getchar(vp
->ed
,mode
==SEARCH
?1:-2)) == usreof
)
1333 else if( c
== usrerase
)
1335 else if( c
== usrkill
)
1337 else if( c
== editb
.e_werase
)
1339 else if( c
== usrlnext
)
1344 /*** implement ^V to escape next char ***/
1345 c
= ed_getchar(vp
->ed
,2);
1353 case ESC
: /** enter control mode **/
1354 if(!sh_isoption(SH_VI
))
1359 if( mode
== SEARCH
)
1367 if( mode
== REPLACE
)
1369 c
= max_virt
-cur_virt
;
1370 if(c
> 0 && last_save
>=cur_virt
)
1372 genncpy((&virtual[cur_virt
]),&saveline
[cur_virt
],c
);
1373 if(last_virt
>=last_save
)
1374 last_virt
=last_save
-1;
1380 if( tmp
== ENTER
|| tmp
== BIGVI
)
1383 vp
->bigvi
= (tmp
==BIGVI
);
1384 #endif /* SHOPT_MULTIBYTE */
1395 c
= last_save
= last_virt
+1;
1398 genncpy(saveline
, virtual, c
);
1403 case UERASE
: /** user erase char **/
1404 /*** treat as backspace ***/
1406 case '\b': /** backspace **/
1407 if( virtual[cur_virt
] == '\\' )
1410 append(vp
,usrerase
, mode
);
1414 if( mode
==SEARCH
&& cur_virt
==0 )
1420 if(mode
==REPLACE
|| (last_save
>0 && last_virt
<=last_save
))
1422 if(cur_virt
<=first_virt
)
1424 else if(mode
==REPLACE
)
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) )
1446 backword(vp
,1, 'W');
1447 cdelete(vp
,tmp
- cur_virt
+ 1, BAD
);
1451 case UKILL
: /** user kill line char **/
1452 if( virtual[cur_virt
] == '\\' )
1455 append(vp
,usrkill
, mode
);
1459 if( mode
== SEARCH
)
1462 delmotion(vp
, '$', BAD
);
1467 cur_virt
= first_virt
;
1468 cdelete(vp
,tmp
- cur_virt
+ 1, BAD
);
1475 case UEOF
: /** eof char **/
1476 if( cur_virt
!= INVALID
)
1480 case '\n': /** newline or return **/
1481 if( mode
!= SEARCH
)
1487 case '\t': /** command completion **/
1488 if(mode
!=SEARCH
&& last_virt
>=0 && (vp
->ed
->e_tabcount
|| !isblank(cur_virt
)) && vp
->ed
->sh
->nextprompt
)
1490 if(vp
->ed
->e_tabcount
==0)
1492 ed_ungetchar(vp
->ed
,'\\');
1493 vp
->ed
->e_tabcount
=1;
1496 else if(vp
->ed
->e_tabcount
==1)
1498 ed_ungetchar(vp
->ed
,'=');
1501 vp
->ed
->e_tabcount
= 0;
1505 if( mode
== REPLACE
)
1507 if( cur_virt
< last_virt
)
1510 if(cur_virt
>max_virt
)
1511 max_virt
= cur_virt
;
1516 max_virt
= last_virt
+3;
1526 /*{ MVCURSOR( motion )
1528 * This routine will move the virtual cursor according to motion
1531 * It returns GOOD if successful; else BAD.
1535 static int mvcursor(register Vi_t
* vp
,register int motion
)
1538 register int tcur_virt
;
1539 register int incr
= -1;
1540 register int bound
= 0;
1544 /***** Cursor move commands *****/
1546 case '0': /** First column **/
1550 case '^': /** First nonblank character **/
1551 tcur_virt
= first_virt
;
1552 while( isblank(tcur_virt
) && tcur_virt
< last_virt
)
1557 tcur_virt
= vp
->repeat
-1;
1558 if(tcur_virt
<= last_virt
)
1562 case '$': /** End of line **/
1563 tcur_virt
= last_virt
;
1567 switch(motion
=getcount(vp
,ed_getchar(vp
->ed
,-1)))
1570 if(cur_virt
>=0 && cur_virt
<(SEARCHSIZE
-2) && cur_virt
== last_virt
)
1572 virtual[last_virt
+ 1] = '\0';
1574 ed_external(virtual,lsearch
+1);
1576 strcpy(lsearch
+1,virtual);
1577 #endif /* SHOPT_MULTIBYTE */
1580 ed_ungetchar(vp
->ed
,'n');
1582 else if(cur_virt
==0 && vp
->direction
== -2)
1583 ed_ungetchar(vp
->ed
,'n');
1585 ed_ungetchar(vp
->ed
,'k');
1588 ed_ungetchar(vp
->ed
,'j');
1595 motion
= first_virt
;
1601 tcur_virt
= last_virt
;
1604 ed_ungetchar(vp
->ed
,motion
);
1609 case 'h': /** Left one **/
1611 motion
= first_virt
;
1615 case 'l': /** Right one **/
1619 tcur_virt
= cur_virt
;
1620 if( incr
*tcur_virt
< motion
)
1622 tcur_virt
+= vp
->repeat
*incr
;
1623 if( incr
*tcur_virt
> motion
)
1631 case 'b': /** back word **/
1632 tcur_virt
= cur_virt
;
1633 backword(vp
,vp
->repeat
, motion
);
1634 if( cur_virt
== tcur_virt
)
1639 case 'e': /** end of word **/
1640 tcur_virt
= cur_virt
;
1642 endword(vp
, vp
->repeat
, motion
);
1643 if( cur_virt
== tcur_virt
)
1647 case ',': /** reverse find old char **/
1648 case ';': /** find old char **/
1649 switch(vp
->last_find
)
1674 case 't': /** find up to new char forward **/
1675 case 'f': /** find new char forward **/
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
)
1685 tcur_virt
= cur_virt
;
1689 while( incr
*(tcur_virt
+=incr
) <= bound
1690 && virtual[tcur_virt
] != vp
->findchar
);
1691 if( incr
*tcur_virt
> bound
)
1696 if( fold(vp
->last_find
) == 'T' )
1704 tcur_virt
= cur_virt
;
1705 while( tcur_virt
<= last_virt
1706 && strchr(paren_chars
,virtual[tcur_virt
])==(char*)0)
1708 if(tcur_virt
> last_virt
)
1710 nextc
= virtual[tcur_virt
];
1711 count
= strchr(paren_chars
,nextc
)-paren_chars
;
1716 nextmotion
= paren_chars
[count
+3];
1719 nextmotion
= paren_chars
[count
-3];
1721 while(count
>0 && incr
*(tcur_virt
+=incr
) <= bound
)
1723 if(virtual[tcur_virt
] == nextmotion
)
1725 else if(virtual[tcur_virt
]==nextc
)
1734 case 'w': /** forward word **/
1735 tcur_virt
= cur_virt
;
1736 forward(vp
,vp
->repeat
, motion
);
1737 if( tcur_virt
== cur_virt
)
1744 cur_virt
= tcur_virt
;
1753 static void pr_string(register Vi_t
*vp
, register const char *sp
)
1755 /*** copy string sp ***/
1756 register char *ptr
= editb
.e_outptr
;
1759 editb
.e_outptr
= ptr
;
1763 /*{ PUTSTRING( column, nchars )
1765 * Put nchars starting at column of physical into the workspace
1770 static void putstring(register Vi_t
*vp
,register int col
, register int nchars
)
1773 putchar(physical
[col
++]);
1779 * This routine will refresh the crt so the physical image matches
1780 * the virtual image and display the proper window.
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.
1789 * +-------------------------------+
1790 * | | | virtual | | |
1791 * +-------------------------------+
1792 * cur_virt last_virt
1794 * +-----------------------------------------------+
1795 * | | | physical | | |
1796 * +-----------------------------------------------+
1797 * cur_phys last_phys
1800 * +-----------------------+
1802 * +-----------------------+
1803 * cur_window = cur_phys - first_wind
1806 static void refresh(register Vi_t
* vp
, int mode
)
1810 register int first_w
= vp
->first_wind
;
1814 int opflag
; /* search optimize flag */
1819 /*** find out if it's necessary to start translating at beginning ***/
1823 p
= previous
[lookahead
-1];
1824 if(p
!= ESC
&& p
!= '\n' && p
!= '\r')
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
))) )
1841 if( !is_print(virtual[v
]) )
1843 /*** avoid double ^'s ***/
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
);
1856 /*** see if this was a translate only ***/
1858 if( mode
== TRANSLATE
)
1861 /*** adjust left margin if necessary ***/
1863 if( ncur_phys
<first_w
|| ncur_phys
>=(first_w
+ w_size
) )
1866 first_w
= ncur_phys
- (w_size
>>1);
1869 vp
->first_wind
= cur_phys
= first_w
;
1872 /*** attempt to optimize search somewhat to find ***/
1873 /*** out where physical and window images differ ***/
1875 if( first_w
==vp
->ofirst_wind
&& ncur_phys
>=vp
->ocur_phys
&& opflag
==1 )
1886 for(; (p
<=last_phys
&& w
<=vp
->last_wind
); ++p
, ++w
)
1888 if( window
[w
] != physical
[p
] )
1893 if( (p
>last_phys
|| p
>=first_w
+w_size
) && w
>vp
->last_wind
1894 && cur_virt
==vp
->ocur_virt
)
1896 /*** images are identical ***/
1900 /*** copy the physical image to the window image ***/
1902 if( last_virt
!= INVALID
)
1904 while( p
<= last_phys
&& w
< w_size
)
1905 window
[w
++] = physical
[p
++];
1909 /*** erase trailing characters if needed ***/
1911 while( w
<= vp
->last_wind
)
1913 vp
->last_wind
= --w
;
1917 /*** move cursor to start of difference ***/
1921 /*** and output difference ***/
1924 while( w
<= vp
->last_wind
)
1925 putchar(window
[w
++]);
1927 cur_phys
= w
+ first_w
;
1928 vp
->last_wind
= --new_lw
;
1930 if( last_phys
>= w_size
)
1933 vp
->long_char
= '>';
1934 else if( last_phys
< (first_w
+w_size
) )
1935 vp
->long_char
= '<';
1937 vp
->long_char
= '*';
1940 vp
->long_char
= ' ';
1942 if( vp
->long_line
!= vp
->long_char
)
1944 /*** indicate lines longer than window ***/
1945 while( w
++ < w_size
)
1950 putchar(vp
->long_char
);
1952 vp
->long_line
= vp
->long_char
;
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
;
1962 if( mode
==INPUT
&& cur_virt
>INVALID
)
1965 cursor(vp
,ncur_phys
);
1970 /*{ REPLACE( char, increment )
1972 * Replace the cur_virt character with char. This routine attempts
1973 * to avoid using refresh().
1975 * increment = 1, increment cur_virt after replacement.
1976 * = 0, leave cur_virt where it is.
1980 static void replace(register Vi_t
*vp
, register int c
, register int increment
)
1982 register int cur_window
;
1984 if( cur_virt
== INVALID
)
1986 /*** can't replace invalid cursor ***/
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
)
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])) )
2001 /*** must use standard refresh routine ***/
2004 append(vp
,c
, APPEND
);
2005 if( increment
&& cur_virt
<last_virt
)
2007 refresh(vp
,CONTROL
);
2011 virtual[cur_virt
] = c
;
2012 physical
[cur_phys
] = c
;
2013 window
[cur_window
] = c
;
2017 c
= virtual[++cur_virt
];
2032 * Restore the contents of virtual space from u_space.
2036 static void restore_v(register Vi_t
*vp
)
2038 register int tmpcol
;
2039 genchar tmpspace
[MAXLINE
];
2041 if( vp
->u_column
== INVALID
-1 )
2043 /*** never saved anything ***/
2047 gencpy(tmpspace
, vp
->u_space
);
2048 tmpcol
= vp
->u_column
;
2050 gencpy(virtual, tmpspace
);
2052 last_virt
= genlen(tmpspace
) - 1;
2053 vp
->ocur_virt
= MAXCHAR
; /** invalidate refresh optimization **/
2059 * If the user has typed something, save it in last line.
2063 static void save_last(register Vi_t
* vp
)
2067 if( (i
= cur_virt
- first_virt
+ 1) > 0 )
2069 /*** save last thing user typed ***/
2072 genncpy(vp
->lastline
, (&virtual[first_virt
]), i
);
2073 vp
->lastline
[i
] = '\0';
2080 * This routine will save the contents of virtual in u_space.
2084 static void save_v(register Vi_t
*vp
)
2088 virtual[last_virt
+ 1] = '\0';
2089 gencpy(vp
->u_space
, virtual);
2090 vp
->u_column
= cur_virt
;
2097 * Search history file for regular expression.
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
2107 * search for <string> in the current command
2109 static int curline_search(Vi_t
*vp
, const char *string
)
2111 register int len
=strlen(string
);
2112 register const char *dp
,*cp
=string
, *dpmax
;
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
++)
2118 if(*dp
==*cp
&& memcmp(cp
,dp
,len
)==0)
2119 return(dp
-(char*)vp
->u_space
);
2122 ed_internal((char*)vp
->u_space
,vp
->u_space
);
2123 #endif /* SHOPT_MULTIBYTE */
2127 static int search(register Vi_t
* vp
,register int mode
)
2129 register int new_direction
;
2130 register int oldcurhline
;
2134 if( vp
->direction
== -2 && mode
!= 'n')
2136 if( mode
== '/' || mode
== '?')
2138 /*** new search expression ***/
2140 append(vp
,mode
, APPEND
);
2145 virtual[last_virt
+ 1] = '\0'; /*** make null terminated ***/
2146 vp
->direction
= mode
=='/' ? -1 : 1;
2149 if( cur_virt
== INVALID
)
2151 /*** no operation ***/
2155 if( cur_virt
==0 || fold(mode
)=='N' )
2157 /*** user wants repeat of last search ***/
2159 strcpy( ((char*)virtual)+1, lsearch
);
2161 *((char*)virtual) = '/';
2162 ed_internal((char*)virtual,virtual);
2163 #endif /* SHOPT_MULTIBYTE */
2167 new_direction
= -vp
->direction
;
2169 new_direction
= vp
->direction
;
2172 /*** now search ***/
2174 oldcurhline
= curhline
;
2176 ed_external(virtual,(char*)virtual);
2177 #endif /* SHOPT_MULTIBYTE */
2178 if(mode
=='?' && (i
=curline_search(vp
,((char*)virtual)+1))>=0)
2180 location
.hist_command
= curhline
;
2181 location
.hist_char
= i
;
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
);
2191 strncpy(lsearch
, ((char*)virtual)+1, SEARCHSIZE
);
2192 if( (curhline
=location
.hist_command
) >=0 )
2194 vp
->ocur_virt
= INVALID
;
2198 /*** could not find matching line ***/
2200 curhline
= oldcurhline
;
2206 * This routine will move the physical cursor to the same
2207 * column as the virtual cursor.
2211 static void sync_cursor(register Vi_t
*vp
)
2218 if( cur_virt
== INVALID
)
2221 /*** find physical col that corresponds to virtual col ***/
2224 if(vp
->first_wind
==vp
->ofirst_wind
&& cur_virt
>vp
->ocur_virt
&& vp
->ocur_virt
!=INVALID
)
2226 /*** try to optimize search a little ***/
2227 p
= vp
->ocur_phys
+ 1;
2229 while(physical
[p
]==MARKER
)
2231 #endif /* SHOPT_MULTIBYTE */
2232 v
= vp
->ocur_virt
+ 1;
2239 for(; v
<= last_virt
; ++p
, ++v
)
2244 if((d
= mbwidth(c
)) > 1)
2249 else if(!iswprint(c
))
2253 #endif /* SHOPT_MULTIBYTE */
2257 p
-= ((p
+editb
.e_plen
)%TABSIZE
);
2272 if( new_phys
< vp
->first_wind
|| new_phys
>= vp
->first_wind
+ w_size
)
2274 /*** asked to move outside of window ***/
2277 refresh(vp
,CONTROL
);
2281 cursor(vp
,new_phys
);
2283 vp
->ocur_phys
= cur_phys
;
2284 vp
->ocur_virt
= cur_virt
;
2285 vp
->o_v_char
= virtual[vp
->ocur_virt
];
2290 /*{ TEXTMOD( command, mode )
2292 * Modify text operations.
2294 * mode != 0, repeat previous operation
2298 static int textmod(register Vi_t
*vp
,register int c
, int mode
)
2301 register genchar
*p
= vp
->lastline
;
2302 register int trepeat
= vp
->repeat
;
2305 if(mode
&& (fold(vp
->lastmotion
)=='F' || fold(vp
->lastmotion
)=='T'))
2306 vp
->lastmotion
= ';';
2308 if( fold(c
) == 'P' )
2310 /*** change p from lastline to yankbuf ***/
2317 /***** Input commands *****/
2321 if(vp
->ed
->e_tabcount
!=1)
2324 case '*': /** do file name expansion in place **/
2325 case '\\': /** do file name completion in place **/
2326 if( cur_virt
== INVALID
)
2328 case '=': /** list file name expansions **/
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)
2336 if(vp
->ed
->e_tabcount
)
2338 vp
->ed
->e_tabcount
=2;
2339 ed_ungetchar(vp
->ed
,'\t');
2346 else if(c
== '=' && !vp
->repeat_set
)
2350 ed_ungetchar(vp
->ed
,cntl('L'));
2357 vp
->ocur_virt
= MAXCHAR
;
2358 if(c
=='=' || (mode
<cur_virt
&& (virtual[cur_virt
]==' ' || virtual[cur_virt
]=='/')))
2359 vp
->ed
->e_tabcount
= 0;
2364 case '@': /** macro expansion **/
2368 if((c
=getrchar(vp
))==ESC
)
2372 if(ed_macro(vp
->ed
,c
))
2382 case '_': /** append last argument of prev command **/
2385 genchar tmpbuf
[MAXLINE
];
2386 if(vp
->repeat_set
==0)
2388 p
= (genchar
*)hist_word((char*)tmpbuf
,MAXLINE
,vp
->repeat
);
2395 ed_internal((char*)p
,tmpbuf
);
2397 #endif /* SHOPT_MULTIBYTE */
2401 append(vp
,i
,APPEND
);
2407 case 'A': /** append to end of line **/
2408 cur_virt
= last_virt
;
2411 case 'a': /** append **/
2412 if( fold(mode
) == 'A' )
2418 if( cur_virt
!= INVALID
)
2420 first_virt
= cur_virt
+ 1;
2421 cursor(vp
,cur_phys
+ 1);
2426 case 'I': /** insert at beginning of line **/
2427 cur_virt
= first_virt
;
2430 case 'i': /** insert **/
2431 if( fold(mode
) == 'I' )
2437 if( cur_virt
!= INVALID
)
2439 vp
->o_v_char
= virtual[cur_virt
];
2440 first_virt
= cur_virt
--;
2444 case 'C': /** change to eol **/
2448 case 'c': /** change **/
2452 c
= getcount(vp
,ed_getchar(vp
->ed
,-1));
2461 if(!delmotion(vp
, c
, 'c'))
2470 first_virt
= cur_virt
+ 1;
2473 case 'D': /** delete to eol **/
2477 case 'd': /** delete **/
2481 c
= getcount(vp
,ed_getchar(vp
->ed
,-1));
2489 if(!delmotion(vp
, c
, 'd'))
2491 if( cur_virt
< last_virt
)
2498 if( cur_virt
!= INVALID
)
2500 i
= virtual[cur_virt
];
2502 vp
->ocur_virt
= INVALID
;
2506 case 'p': /** print **/
2510 if( mode
!= 's' && mode
!= 'c' )
2515 /*** fix stored cur_virt ***/
2524 for(i
=0; i
<trepeat
; ++i
)
2532 case 'R': /* Replace many chars **/
2539 if( cur_virt
!= INVALID
)
2540 first_virt
= cur_virt
;
2543 case 'r': /** replace **/
2547 if((c
=getrchar(vp
))==ESC
)
2552 replace(vp
,c
, trepeat
!=0);
2555 case 'S': /** Substitute line - cc **/
2559 case 's': /** substitute **/
2561 cdelete(vp
,vp
->repeat
, BAD
);
2568 first_virt
= cur_virt
+ 1;
2571 case 'Y': /** Yank to end of line **/
2575 case 'y': /** yank thru motion **/
2579 c
= getcount(vp
,ed_getchar(vp
->ed
,-1));
2584 gencpy(yankbuf
, virtual);
2586 else if(!delmotion(vp
, c
, 'y'))
2592 case 'x': /** delete repeat chars forward - dl **/
2596 case 'X': /** delete repeat chars backward - dh **/
2600 case '~': /** invert case and advance **/
2601 if( cur_virt
!= INVALID
)
2605 while(trepeat
-->0 && i
!=cur_virt
)
2608 c
= virtual[cur_virt
];
2611 #endif /* SHOPT_MULTIBYTE */
2614 else if( islower(c
) )
2626 refresh(vp
,CONTROL
);
2632 static int _isalph(register int v
)
2634 #ifdef _lib_iswalnum
2635 return(iswalnum(v
) || v
=='_');
2637 return((v
&~STRIP
) || isalnum(v
) || v
=='_');
2642 static int _isblank(register int v
)
2644 return((v
&~STRIP
)==0 && isspace(v
));
2647 static int _ismetach(register int v
)
2649 return((v
&~STRIP
)==0 && ismeta(v
));
2652 #endif /* SHOPT_MULTIBYTE */
2655 * get a character, after ^V processing
2657 static int getrchar(register Vi_t
*vp
)
2660 if((c
=ed_getchar(vp
->ed
,1))== usrlnext
)
2661 c
= ed_getchar(vp
->ed
,2);