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