Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/ksh93/edit/vi.c
1810 views
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 <[email protected]> *
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
*/
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
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
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
707
static int cntlmode(Vi_t *vp)
708
{
709
register int c;
710
register int i;
711
genchar tmp_u_space[MAXLINE]; /* temporary u_space */
712
genchar *real_u_space; /* points to real u_space */
713
int tmp_u_column = INVALID; /* temporary u_column */
714
int was_inmacro;
715
716
if(!vp->U_saved)
717
{
718
/*** save virtual image if never done before ***/
719
virtual[last_virt+1] = '\0';
720
gencpy(vp->U_space, virtual);
721
vp->U_saved = 1;
722
}
723
724
save_last(vp);
725
726
real_u_space = vp->u_space;
727
curhline = histmax;
728
first_virt = 0;
729
vp->repeat = 1;
730
if( cur_virt > INVALID )
731
{
732
/*** make sure cursor is at the last char ***/
733
sync_cursor(vp);
734
}
735
else if(last_virt > INVALID )
736
cur_virt++;
737
738
/*** Read control char until something happens to cause a ***/
739
/* return to APPEND/REPLACE mode */
740
741
while( c=ed_getchar(vp->ed,-1) )
742
{
743
vp->repeat_set = 0;
744
was_inmacro = inmacro;
745
if( c == '0' )
746
{
747
/*** move to leftmost column ***/
748
cur_virt = 0;
749
sync_cursor(vp);
750
continue;
751
}
752
753
if( digit(c) )
754
{
755
c = getcount(vp,c);
756
if( c == '.' )
757
vp->lastrepeat = vp->repeat;
758
}
759
760
/*** see if it's a move cursor command ***/
761
762
if(mvcursor(vp,c))
763
{
764
sync_cursor(vp);
765
vp->repeat = 1;
766
continue;
767
}
768
769
/*** see if it's a repeat of the last command ***/
770
771
if( c == '.' )
772
{
773
c = vp->last_cmd;
774
vp->repeat = vp->lastrepeat;
775
i = textmod(vp,c, c);
776
}
777
else
778
{
779
i = textmod(vp,c, 0);
780
}
781
782
/*** see if it's a text modification command ***/
783
784
switch(i)
785
{
786
case BAD:
787
break;
788
789
default: /** input mode **/
790
if(!was_inmacro)
791
{
792
vp->last_cmd = c;
793
vp->lastrepeat = vp->repeat;
794
}
795
vp->repeat = 1;
796
if( i == GOOD )
797
continue;
798
return(i);
799
}
800
801
switch( c )
802
{
803
/***** Other stuff *****/
804
805
case cntl('L'): /** Redraw line **/
806
/*** print the prompt and ***/
807
/* force a total refresh */
808
if(vp->nonewline==0 && !vp->ed->e_nocrnl)
809
putchar('\n');
810
vp->nonewline = 0;
811
pr_string(vp,Prompt);
812
window[0] = '\0';
813
cur_phys = vp->first_wind;
814
vp->ofirst_wind = INVALID;
815
vp->long_line = ' ';
816
break;
817
818
case cntl('V'):
819
{
820
register const char *p = fmtident(e_version);
821
save_v(vp);
822
del_line(vp,BAD);
823
while(c = *p++)
824
append(vp,c,APPEND);
825
refresh(vp,CONTROL);
826
ed_getchar(vp->ed,-1);
827
restore_v(vp);
828
ed_ungetchar(vp->ed,'a');
829
break;
830
}
831
832
case '/': /** Search **/
833
case '?':
834
case 'N':
835
case 'n':
836
save_v(vp);
837
switch( search(vp,c) )
838
{
839
case GOOD:
840
/*** force a total refresh ***/
841
window[0] = '\0';
842
goto newhist;
843
844
case BAD:
845
/*** no match ***/
846
ed_ringbell();
847
848
default:
849
if( vp->u_column == INVALID )
850
del_line(vp,BAD);
851
else
852
restore_v(vp);
853
break;
854
}
855
break;
856
857
case 'j': /** get next command **/
858
case '+': /** get next command **/
859
#if SHOPT_EDPREDICT
860
if(vp->ed->hlist)
861
{
862
if(vp->ed->hoff >= vp->ed->hmax)
863
goto ringbell;
864
vp->ed->hoff++;
865
goto hupdate;
866
}
867
#endif /* SHOPT_EDPREDICT */
868
curhline += vp->repeat;
869
if( curhline > histmax )
870
{
871
curhline = histmax;
872
goto ringbell;
873
}
874
else if(curhline==histmax && tmp_u_column!=INVALID )
875
{
876
vp->u_space = tmp_u_space;
877
vp->u_column = tmp_u_column;
878
restore_v(vp);
879
vp->u_space = real_u_space;
880
break;
881
}
882
save_v(vp);
883
cur_virt = INVALID;
884
goto newhist;
885
886
case 'k': /** get previous command **/
887
case '-': /** get previous command **/
888
#if SHOPT_EDPREDICT
889
if(vp->ed->hlist)
890
{
891
if(vp->ed->hoff == 0)
892
goto ringbell;
893
vp->ed->hoff--;
894
hupdate:
895
ed_histlist(vp->ed,*vp->ed->hlist!=0);
896
vp->nonewline++;
897
ed_ungetchar(vp->ed,cntl('L'));
898
continue;
899
}
900
#endif /* SHOPT_EDPREDICT */
901
if( curhline == histmax )
902
{
903
vp->u_space = tmp_u_space;
904
i = vp->u_column;
905
save_v(vp);
906
vp->u_space = real_u_space;
907
tmp_u_column = vp->u_column;
908
vp->u_column = i;
909
}
910
911
curhline -= vp->repeat;
912
if( curhline <= histmin )
913
{
914
curhline += vp->repeat;
915
goto ringbell;
916
}
917
save_v(vp);
918
cur_virt = INVALID;
919
newhist:
920
if(curhline!=histmax || cur_virt==INVALID)
921
hist_copy((char*)virtual, MAXLINE, curhline,-1);
922
else
923
{
924
strcpy((char*)virtual,(char*)vp->u_space);
925
#if SHOPT_MULTIBYTE
926
ed_internal((char*)vp->u_space,vp->u_space);
927
#endif /* SHOPT_MULTIBYTE */
928
}
929
#if SHOPT_MULTIBYTE
930
ed_internal((char*)virtual,virtual);
931
#endif /* SHOPT_MULTIBYTE */
932
if((last_virt=genlen(virtual)-1) >= 0 && cur_virt == INVALID)
933
cur_virt = 0;
934
#if SHOPT_EDPREDICT
935
if(vp->ed->hlist)
936
{
937
ed_histlist(vp->ed,0);
938
if(c=='\n')
939
ed_ungetchar(vp->ed,c);
940
ed_ungetchar(vp->ed,cntl('L'));
941
vp->nonewline = 1;
942
cur_virt = 0;
943
}
944
#endif /*SHOPT_EDPREDICT */
945
break;
946
947
948
case 'u': /** undo the last thing done **/
949
restore_v(vp);
950
break;
951
952
case 'U': /** Undo everything **/
953
save_v(vp);
954
if( virtual[0] == '\0' )
955
goto ringbell;
956
else
957
{
958
gencpy(virtual, vp->U_space);
959
last_virt = genlen(vp->U_space) - 1;
960
cur_virt = 0;
961
}
962
break;
963
964
#if KSHELL
965
case 'v':
966
if(vp->repeat_set==0)
967
goto vcommand;
968
#endif /* KSHELL */
969
970
case 'G': /** goto command repeat **/
971
if(vp->repeat_set==0)
972
vp->repeat = histmin+1;
973
if( vp->repeat <= histmin || vp->repeat > histmax )
974
{
975
goto ringbell;
976
}
977
curhline = vp->repeat;
978
save_v(vp);
979
if(c == 'G')
980
{
981
cur_virt = INVALID;
982
goto newhist;
983
}
984
985
#if KSHELL
986
vcommand:
987
if(ed_fulledit(vp->ed)==GOOD)
988
return(BIGVI);
989
else
990
goto ringbell;
991
#endif /* KSHELL */
992
993
case '#': /** insert(delete) # to (no)comment command **/
994
if( cur_virt != INVALID )
995
{
996
register genchar *p = &virtual[last_virt+1];
997
*p = 0;
998
/*** see whether first char is comment char ***/
999
c = (virtual[0]=='#');
1000
while(p-- >= virtual)
1001
{
1002
if(*p=='\n' || p<virtual)
1003
{
1004
if(c) /* delete '#' */
1005
{
1006
if(p[1]=='#')
1007
{
1008
last_virt--;
1009
gencpy(p+1,p+2);
1010
}
1011
}
1012
else
1013
{
1014
cur_virt = p-virtual;
1015
append(vp,'#', APPEND);
1016
}
1017
}
1018
}
1019
if(c)
1020
{
1021
curhline = histmax;
1022
cur_virt = 0;
1023
break;
1024
}
1025
refresh(vp,INPUT);
1026
}
1027
1028
case '\n': /** send to shell **/
1029
#if SHOPT_EDPREDICT
1030
if(!vp->ed->hlist)
1031
return(ENTER);
1032
case '\t': /** bring choice to edit **/
1033
if(vp->ed->hlist)
1034
{
1035
if(vp->repeat > vp->ed->nhlist-vp->ed->hoff)
1036
goto ringbell;
1037
curhline = vp->ed->hlist[vp->repeat+vp->ed->hoff-1]->index;
1038
goto newhist;
1039
}
1040
goto ringbell;
1041
#else
1042
return(ENTER);
1043
#endif /* SHOPT_EDPREDICT */
1044
case ESC:
1045
/* don't ring bell if next char is '[' */
1046
if(!lookahead)
1047
{
1048
char x;
1049
if(sfpkrd(editb.e_fd,&x,1,'\r',400L,-1)>0)
1050
ed_ungetchar(vp->ed,x);
1051
}
1052
if(lookahead)
1053
{
1054
ed_ungetchar(vp->ed,c=ed_getchar(vp->ed,1));
1055
if(c=='[')
1056
{
1057
vp->repeat = 1;
1058
continue;
1059
}
1060
}
1061
default:
1062
ringbell:
1063
ed_ringbell();
1064
vp->repeat = 1;
1065
continue;
1066
}
1067
1068
refresh(vp,CONTROL);
1069
vp->repeat = 1;
1070
}
1071
/* NOTREACHED */
1072
return(0);
1073
}
1074
1075
/*{ CURSOR( new_current_physical )
1076
*
1077
* This routine will position the virtual cursor at
1078
* physical column x in the window.
1079
*
1080
}*/
1081
1082
static void cursor(Vi_t *vp,register int x)
1083
{
1084
#if SHOPT_MULTIBYTE
1085
while(physical[x]==MARKER)
1086
x++;
1087
#endif /* SHOPT_MULTIBYTE */
1088
cur_phys = ed_setcursor(vp->ed, physical, cur_phys,x,vp->first_wind);
1089
}
1090
1091
/*{ DELETE( nchars, mode )
1092
*
1093
* Delete nchars from the virtual space and leave cur_virt positioned
1094
* at cur_virt-1.
1095
*
1096
* If mode = 'c', do not save the characters deleted
1097
* = 'd', save them in yankbuf and delete.
1098
* = 'y', save them in yankbuf but do not delete.
1099
*
1100
}*/
1101
1102
static void cdelete(Vi_t *vp,register int nchars, int mode)
1103
{
1104
register int i;
1105
register genchar *cp;
1106
1107
if( cur_virt < first_virt )
1108
{
1109
ed_ringbell();
1110
return;
1111
}
1112
if( nchars > 0 )
1113
{
1114
cp = virtual+cur_virt;
1115
vp->o_v_char = cp[0];
1116
if( (cur_virt-- + nchars) > last_virt )
1117
{
1118
/*** set nchars to number actually deleted ***/
1119
nchars = last_virt - cur_virt;
1120
}
1121
1122
/*** save characters to be deleted ***/
1123
1124
if( mode != 'c' )
1125
{
1126
i = cp[nchars];
1127
cp[nchars] = 0;
1128
gencpy(yankbuf,cp);
1129
cp[nchars] = i;
1130
}
1131
1132
/*** now delete these characters ***/
1133
1134
if( mode != 'y' )
1135
{
1136
gencpy(cp,cp+nchars);
1137
last_virt -= nchars;
1138
}
1139
}
1140
return;
1141
}
1142
1143
/*{ DEL_LINE( mode )
1144
*
1145
* This routine will delete the line.
1146
* mode = GOOD, do a save_v()
1147
*
1148
}*/
1149
static void del_line(register Vi_t *vp, int mode)
1150
{
1151
if( last_virt == INVALID )
1152
return;
1153
1154
if( mode == GOOD )
1155
save_v(vp);
1156
1157
cur_virt = 0;
1158
first_virt = 0;
1159
cdelete(vp,last_virt+1, BAD);
1160
refresh(vp,CONTROL);
1161
1162
cur_virt = INVALID;
1163
cur_phys = 0;
1164
vp->findchar = INVALID;
1165
last_phys = INVALID;
1166
last_virt = INVALID;
1167
vp->last_wind = INVALID;
1168
vp->first_wind = 0;
1169
vp->o_v_char = '\0';
1170
vp->ocur_phys = 0;
1171
vp->ocur_virt = MAXCHAR;
1172
vp->ofirst_wind = 0;
1173
window[0] = '\0';
1174
return;
1175
}
1176
1177
/*{ DELMOTION( motion, mode )
1178
*
1179
* Delete thru motion.
1180
*
1181
* mode = 'd', save deleted characters, delete
1182
* = 'c', do not save characters, change
1183
* = 'y', save characters, yank
1184
*
1185
* Returns 1 if operation successful; else 0.
1186
*
1187
}*/
1188
1189
static int delmotion(Vi_t *vp,int motion, int mode)
1190
{
1191
register int begin, end, delta;
1192
/* the following saves a register */
1193
1194
if( cur_virt == INVALID )
1195
return(0);
1196
if( mode != 'y' )
1197
save_v(vp);
1198
begin = cur_virt;
1199
1200
/*** fake out the motion routines by appending a blank ***/
1201
1202
virtual[++last_virt] = ' ';
1203
end = mvcursor(vp,motion);
1204
virtual[last_virt--] = 0;
1205
if(!end)
1206
return(0);
1207
1208
end = cur_virt;
1209
if( mode=='c' && end>begin && strchr("wW", motion) )
1210
{
1211
/*** called by change operation, user really expects ***/
1212
/* the effect of the eE commands, so back up to end of word */
1213
while( end>begin && isblank(end-1) )
1214
--end;
1215
if( end == begin )
1216
++end;
1217
}
1218
1219
delta = end - begin;
1220
if( delta >= 0 )
1221
{
1222
cur_virt = begin;
1223
if( strchr("eE;,TtFf%", motion) )
1224
++delta;
1225
}
1226
else
1227
{
1228
delta = -delta + (motion=='%');
1229
}
1230
1231
cdelete(vp,delta, mode);
1232
if( mode == 'y' )
1233
cur_virt = begin;
1234
return(1);
1235
}
1236
1237
1238
/*{ ENDWORD( nwords, cmd )
1239
*
1240
* This routine will move cur_virt to the end of the nth word.
1241
*
1242
}*/
1243
1244
static void endword(Vi_t *vp, int nwords, register int cmd)
1245
{
1246
register int tcur_virt = cur_virt;
1247
while( nwords-- )
1248
{
1249
if( !isblank(tcur_virt) && tcur_virt<=last_virt )
1250
++tcur_virt;
1251
while( isblank(tcur_virt) && tcur_virt<=last_virt )
1252
++tcur_virt;
1253
if( cmd == 'E' )
1254
{
1255
while( !isblank(tcur_virt) && tcur_virt<=last_virt )
1256
++tcur_virt;
1257
}
1258
else
1259
{
1260
if( isalph(tcur_virt) )
1261
while( isalph(tcur_virt) && tcur_virt<=last_virt )
1262
++tcur_virt;
1263
else
1264
while( !isalph(tcur_virt) && !isblank(tcur_virt)
1265
&& tcur_virt<=last_virt )
1266
++tcur_virt;
1267
}
1268
if( tcur_virt > first_virt )
1269
tcur_virt--;
1270
}
1271
cur_virt = tcur_virt;
1272
return;
1273
}
1274
1275
/*{ FORWARD( nwords, cmd )
1276
*
1277
* This routine will move cur_virt forward to the next nth word.
1278
*
1279
}*/
1280
1281
static void forward(Vi_t *vp,register int nwords, int cmd)
1282
{
1283
register int tcur_virt = cur_virt;
1284
while( nwords-- )
1285
{
1286
if( cmd == 'W' )
1287
{
1288
while( !isblank(tcur_virt) && tcur_virt < last_virt )
1289
++tcur_virt;
1290
}
1291
else
1292
{
1293
if( isalph(tcur_virt) )
1294
{
1295
while( isalph(tcur_virt) && tcur_virt<last_virt )
1296
++tcur_virt;
1297
}
1298
else
1299
{
1300
while( !isalph(tcur_virt) && !isblank(tcur_virt)
1301
&& tcur_virt < last_virt )
1302
++tcur_virt;
1303
}
1304
}
1305
while( isblank(tcur_virt) && tcur_virt < last_virt )
1306
++tcur_virt;
1307
}
1308
cur_virt = tcur_virt;
1309
return;
1310
}
1311
1312
1313
1314
/*{ GETCOUNT(c)
1315
*
1316
* Set repeat to the user typed number and return the terminating
1317
* character.
1318
*
1319
}*/
1320
1321
static int getcount(register Vi_t *vp,register int c)
1322
{
1323
register int i;
1324
1325
/*** get any repeat count ***/
1326
1327
if( c == '0' )
1328
return(c);
1329
1330
vp->repeat_set++;
1331
i = 0;
1332
while( digit(c) )
1333
{
1334
i = i*10 + c - '0';
1335
c = ed_getchar(vp->ed,-1);
1336
}
1337
1338
if( i > 0 )
1339
vp->repeat *= i;
1340
return(c);
1341
}
1342
1343
1344
/*{ GETLINE( mode )
1345
*
1346
* This routine will fetch a line.
1347
* mode = APPEND, allow escape to cntlmode subroutine
1348
* appending characters.
1349
* = REPLACE, allow escape to cntlmode subroutine
1350
* replacing characters.
1351
* = SEARCH, no escape allowed
1352
* = ESC, enter control mode immediately
1353
*
1354
* The cursor will always be positioned after the last
1355
* char printed.
1356
*
1357
* This routine returns when cr, nl, or (eof in column 0) is
1358
* received (column 0 is the first char position).
1359
*
1360
}*/
1361
1362
static void getline(register Vi_t* vp,register int mode)
1363
{
1364
register int c;
1365
register int tmp;
1366
int max_virt=0, last_save=0;
1367
genchar saveline[MAXLINE];
1368
vp->addnl = 1;
1369
1370
if( mode == ESC )
1371
{
1372
/*** go directly to control mode ***/
1373
goto escape;
1374
}
1375
1376
for(;;)
1377
{
1378
if( (c=ed_getchar(vp->ed,mode==SEARCH?1:-2)) == usreof )
1379
c = UEOF;
1380
else if( c == usrerase )
1381
c = UERASE;
1382
else if( c == usrkill )
1383
c = UKILL;
1384
else if( c == editb.e_werase )
1385
c = UWERASE;
1386
else if( c == usrlnext )
1387
c = ULNEXT;
1388
else if(mode==SEARCH && c==editb.e_intr)
1389
c = UINTR;
1390
1391
if( c == ULNEXT)
1392
{
1393
/*** implement ^V to escape next char ***/
1394
c = ed_getchar(vp->ed,2);
1395
append(vp,c, mode);
1396
refresh(vp,INPUT);
1397
continue;
1398
}
1399
if(c!='\t')
1400
vp->ed->e_tabcount = 0;
1401
1402
switch( c )
1403
{
1404
case ESC: /** enter control mode **/
1405
if(!sh_isoption(SH_VI))
1406
{
1407
append(vp,c, mode);
1408
break;
1409
}
1410
if( mode == SEARCH )
1411
{
1412
ed_ringbell();
1413
continue;
1414
}
1415
else
1416
{
1417
escape:
1418
if( mode == REPLACE )
1419
{
1420
c = max_virt-cur_virt;
1421
if(c > 0 && last_save>=cur_virt)
1422
{
1423
genncpy((&virtual[cur_virt]),&saveline[cur_virt],c);
1424
if(last_virt>=last_save)
1425
last_virt=last_save-1;
1426
refresh(vp,INPUT);
1427
}
1428
--cur_virt;
1429
}
1430
tmp = cntlmode(vp);
1431
if( tmp == ENTER || tmp == BIGVI )
1432
{
1433
#if SHOPT_MULTIBYTE
1434
vp->bigvi = (tmp==BIGVI);
1435
#endif /* SHOPT_MULTIBYTE */
1436
return;
1437
}
1438
if( tmp == INSERT )
1439
{
1440
mode = APPEND;
1441
continue;
1442
}
1443
mode = tmp;
1444
if(mode==REPLACE)
1445
{
1446
c = last_save = last_virt+1;
1447
if(c >= MAXLINE)
1448
c = MAXLINE-1;
1449
genncpy(saveline, virtual, c);
1450
}
1451
}
1452
break;
1453
1454
case UINTR:
1455
first_virt = 0;
1456
cdelete(vp,cur_virt+1, BAD);
1457
cur_virt = -1;
1458
return;
1459
case UERASE: /** user erase char **/
1460
/*** treat as backspace ***/
1461
1462
case '\b': /** backspace **/
1463
if( virtual[cur_virt] == '\\' )
1464
{
1465
cdelete(vp,1, BAD);
1466
append(vp,usrerase, mode);
1467
}
1468
else
1469
{
1470
if( mode==SEARCH && cur_virt==0 )
1471
{
1472
first_virt = 0;
1473
cdelete(vp,1, BAD);
1474
return;
1475
}
1476
if(mode==REPLACE || (last_save>0 && last_virt<=last_save))
1477
{
1478
if(cur_virt<=first_virt)
1479
ed_ringbell();
1480
else if(mode==REPLACE)
1481
--cur_virt;
1482
mode = REPLACE;
1483
sync_cursor(vp);
1484
continue;
1485
}
1486
else
1487
cdelete(vp,1, BAD);
1488
}
1489
break;
1490
1491
case UWERASE: /** delete back word **/
1492
if( cur_virt > first_virt &&
1493
!isblank(cur_virt) &&
1494
!ispunct(virtual[cur_virt]) &&
1495
isblank(cur_virt-1) )
1496
{
1497
cdelete(vp,1, BAD);
1498
}
1499
else
1500
{
1501
tmp = cur_virt;
1502
backword(vp,1, 'W');
1503
cdelete(vp,tmp - cur_virt + 1, BAD);
1504
}
1505
break;
1506
1507
case UKILL: /** user kill line char **/
1508
if( virtual[cur_virt] == '\\' )
1509
{
1510
cdelete(vp,1, BAD);
1511
append(vp,usrkill, mode);
1512
}
1513
else
1514
{
1515
if( mode == SEARCH )
1516
{
1517
cur_virt = 1;
1518
delmotion(vp, '$', BAD);
1519
}
1520
else if(first_virt)
1521
{
1522
tmp = cur_virt;
1523
cur_virt = first_virt;
1524
cdelete(vp,tmp - cur_virt + 1, BAD);
1525
}
1526
else
1527
del_line(vp,GOOD);
1528
}
1529
break;
1530
1531
case UEOF: /** eof char **/
1532
if( cur_virt != INVALID )
1533
continue;
1534
vp->addnl = 0;
1535
1536
case '\n': /** newline or return **/
1537
if( mode != SEARCH )
1538
save_last(vp);
1539
refresh(vp,INPUT);
1540
physical[++last_phys] = 0;
1541
return;
1542
1543
case '\t': /** command completion **/
1544
if(mode!=SEARCH && last_virt>=0 && (vp->ed->e_tabcount|| !isblank(cur_virt)) && vp->ed->sh->nextprompt)
1545
{
1546
if(virtual[cur_virt]=='\\')
1547
{
1548
virtual[cur_virt] = '\t';
1549
break;
1550
}
1551
if(vp->ed->e_tabcount==0)
1552
{
1553
ed_ungetchar(vp->ed,'\\');
1554
vp->ed->e_tabcount=1;
1555
goto escape;
1556
}
1557
else if(vp->ed->e_tabcount==1)
1558
{
1559
ed_ungetchar(vp->ed,'=');
1560
goto escape;
1561
}
1562
vp->ed->e_tabcount = 0;
1563
}
1564
/* FALL THRU*/
1565
default:
1566
if( mode == REPLACE )
1567
{
1568
if( cur_virt < last_virt )
1569
{
1570
replace(vp,c, 1);
1571
if(cur_virt>max_virt)
1572
max_virt = cur_virt;
1573
continue;
1574
}
1575
cdelete(vp,1, BAD);
1576
mode = APPEND;
1577
max_virt = last_virt+3;
1578
}
1579
append(vp,c, mode);
1580
break;
1581
}
1582
refresh(vp,INPUT);
1583
1584
}
1585
}
1586
1587
/*{ MVCURSOR( motion )
1588
*
1589
* This routine will move the virtual cursor according to motion
1590
* for repeat times.
1591
*
1592
* It returns GOOD if successful; else BAD.
1593
*
1594
}*/
1595
1596
static int mvcursor(register Vi_t* vp,register int motion)
1597
{
1598
register int count;
1599
register int tcur_virt;
1600
register int incr = -1;
1601
register int bound = 0;
1602
1603
switch(motion)
1604
{
1605
/***** Cursor move commands *****/
1606
1607
case '0': /** First column **/
1608
tcur_virt = 0;
1609
break;
1610
1611
case '^': /** First nonblank character **/
1612
tcur_virt = first_virt;
1613
while( isblank(tcur_virt) && tcur_virt < last_virt )
1614
++tcur_virt;
1615
break;
1616
1617
case '|':
1618
tcur_virt = vp->repeat-1;
1619
if(tcur_virt <= last_virt)
1620
break;
1621
/* fall through */
1622
1623
case '$': /** End of line **/
1624
tcur_virt = last_virt;
1625
break;
1626
1627
case '[':
1628
switch(motion=getcount(vp,ed_getchar(vp->ed,-1)))
1629
{
1630
case 'A':
1631
#if SHOPT_EDPREDICT
1632
if(!vp->ed->hlist && cur_virt>=0 && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt)
1633
#else
1634
if(cur_virt>=0 && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt)
1635
#endif /* SHOPT_EDPREDICT */
1636
{
1637
virtual[last_virt + 1] = '\0';
1638
#if SHOPT_MULTIBYTE
1639
ed_external(virtual,lsearch+1);
1640
#else
1641
strcpy(lsearch+1,virtual);
1642
#endif /* SHOPT_MULTIBYTE */
1643
*lsearch = '^';
1644
vp->direction = -2;
1645
ed_ungetchar(vp->ed,'n');
1646
}
1647
else if(cur_virt==0 && vp->direction == -2)
1648
ed_ungetchar(vp->ed,'n');
1649
else
1650
ed_ungetchar(vp->ed,'k');
1651
return(1);
1652
case 'B':
1653
ed_ungetchar(vp->ed,'j');
1654
return(1);
1655
case 'C':
1656
motion = last_virt;
1657
incr = 1;
1658
goto walk;
1659
case 'D':
1660
motion = first_virt;
1661
goto walk;
1662
case 'H':
1663
tcur_virt = 0;
1664
break;
1665
case 'Y':
1666
tcur_virt = last_virt;
1667
break;
1668
default:
1669
ed_ungetchar(vp->ed,motion);
1670
return(0);
1671
}
1672
break;
1673
1674
case 'h': /** Left one **/
1675
case '\b':
1676
motion = first_virt;
1677
goto walk;
1678
1679
case ' ':
1680
case 'l': /** Right one **/
1681
motion = last_virt;
1682
incr = 1;
1683
walk:
1684
tcur_virt = cur_virt;
1685
if( incr*tcur_virt < motion)
1686
{
1687
tcur_virt += vp->repeat*incr;
1688
if( incr*tcur_virt > motion)
1689
tcur_virt = motion;
1690
}
1691
else
1692
return(0);
1693
break;
1694
1695
case 'B':
1696
case 'b': /** back word **/
1697
tcur_virt = cur_virt;
1698
backword(vp,vp->repeat, motion);
1699
if( cur_virt == tcur_virt )
1700
return(0);
1701
return(1);
1702
1703
case 'E':
1704
case 'e': /** end of word **/
1705
tcur_virt = cur_virt;
1706
if(tcur_virt >=0)
1707
endword(vp, vp->repeat, motion);
1708
if( cur_virt == tcur_virt )
1709
return(0);
1710
return(1);
1711
1712
case ',': /** reverse find old char **/
1713
case ';': /** find old char **/
1714
switch(vp->last_find)
1715
{
1716
case 't':
1717
case 'f':
1718
if(motion==';')
1719
{
1720
bound = last_virt;
1721
incr = 1;
1722
}
1723
goto find_b;
1724
1725
case 'T':
1726
case 'F':
1727
if(motion==',')
1728
{
1729
bound = last_virt;
1730
incr = 1;
1731
}
1732
goto find_b;
1733
1734
default:
1735
return(0);
1736
}
1737
1738
1739
case 't': /** find up to new char forward **/
1740
case 'f': /** find new char forward **/
1741
bound = last_virt;
1742
incr = 1;
1743
1744
case 'T': /** find up to new char backward **/
1745
case 'F': /** find new char backward **/
1746
vp->last_find = motion;
1747
if((vp->findchar=getrchar(vp))==ESC)
1748
return(1);
1749
find_b:
1750
tcur_virt = cur_virt;
1751
count = vp->repeat;
1752
while( count-- )
1753
{
1754
while( incr*(tcur_virt+=incr) <= bound
1755
&& virtual[tcur_virt] != vp->findchar );
1756
if( incr*tcur_virt > bound )
1757
{
1758
return(0);
1759
}
1760
}
1761
if( fold(vp->last_find) == 'T' )
1762
tcur_virt -= incr;
1763
break;
1764
1765
case '%':
1766
{
1767
int nextmotion;
1768
int nextc;
1769
tcur_virt = cur_virt;
1770
while( tcur_virt <= last_virt
1771
&& strchr(paren_chars,virtual[tcur_virt])==(char*)0)
1772
tcur_virt++;
1773
if(tcur_virt > last_virt )
1774
return(0);
1775
nextc = virtual[tcur_virt];
1776
count = strchr(paren_chars,nextc)-paren_chars;
1777
if(count < 3)
1778
{
1779
incr = 1;
1780
bound = last_virt;
1781
nextmotion = paren_chars[count+3];
1782
}
1783
else
1784
nextmotion = paren_chars[count-3];
1785
count = 1;
1786
while(count >0 && incr*(tcur_virt+=incr) <= bound)
1787
{
1788
if(virtual[tcur_virt] == nextmotion)
1789
count--;
1790
else if(virtual[tcur_virt]==nextc)
1791
count++;
1792
}
1793
if(count)
1794
return(0);
1795
break;
1796
}
1797
1798
case 'W':
1799
case 'w': /** forward word **/
1800
tcur_virt = cur_virt;
1801
forward(vp,vp->repeat, motion);
1802
if( tcur_virt == cur_virt )
1803
return(0);
1804
return(1);
1805
1806
default:
1807
return(0);
1808
}
1809
cur_virt = tcur_virt;
1810
1811
return(1);
1812
}
1813
1814
/*
1815
* print a string
1816
*/
1817
1818
static void pr_string(register Vi_t *vp, register const char *sp)
1819
{
1820
/*** copy string sp ***/
1821
register char *ptr = editb.e_outptr;
1822
while(*sp)
1823
*ptr++ = *sp++;
1824
editb.e_outptr = ptr;
1825
return;
1826
}
1827
1828
/*{ PUTSTRING( column, nchars )
1829
*
1830
* Put nchars starting at column of physical into the workspace
1831
* to be printed.
1832
*
1833
}*/
1834
1835
static void putstring(register Vi_t *vp,register int col, register int nchars)
1836
{
1837
while( nchars-- )
1838
putchar(physical[col++]);
1839
return;
1840
}
1841
1842
/*{ REFRESH( mode )
1843
*
1844
* This routine will refresh the crt so the physical image matches
1845
* the virtual image and display the proper window.
1846
*
1847
* mode = CONTROL, refresh in control mode, ie. leave cursor
1848
* positioned at last char printed.
1849
* = INPUT, refresh in input mode; leave cursor positioned
1850
* after last char printed.
1851
* = TRANSLATE, perform virtual to physical translation
1852
* and adjust left margin only.
1853
*
1854
* +-------------------------------+
1855
* | | | virtual | | |
1856
* +-------------------------------+
1857
* cur_virt last_virt
1858
*
1859
* +-----------------------------------------------+
1860
* | | | physical | | |
1861
* +-----------------------------------------------+
1862
* cur_phys last_phys
1863
*
1864
* 0 w_size - 1
1865
* +-----------------------+
1866
* | | | window |
1867
* +-----------------------+
1868
* cur_window = cur_phys - first_wind
1869
}*/
1870
1871
static void refresh(register Vi_t* vp, int mode)
1872
{
1873
register int p;
1874
register int v;
1875
register int first_w = vp->first_wind;
1876
int p_differ;
1877
int new_lw;
1878
int ncur_phys;
1879
int opflag; /* search optimize flag */
1880
1881
# define w v
1882
1883
/*** find out if it's necessary to start translating at beginning ***/
1884
1885
if(lookahead>0)
1886
{
1887
p = previous[lookahead-1];
1888
if(p != ESC && p != '\n' && p != '\r')
1889
mode = TRANSLATE;
1890
}
1891
v = cur_virt;
1892
#if SHOPT_EDPREDICT
1893
if(mode==INPUT && v>0 && virtual[0]=='#' && v==last_virt && virtual[v]!='*' && sh_isoption(SH_VI))
1894
{
1895
int n;
1896
virtual[last_virt+1] = 0;
1897
# if SHOPT_MULTIBYTE
1898
ed_external(virtual,(char*)virtual);
1899
# endif /* SHOPT_MULTIBYTE */
1900
n = ed_histgen(vp->ed,(char*)virtual);
1901
# if SHOPT_MULTIBYTE
1902
ed_internal((char*)virtual,virtual);
1903
# endif /* SHOPT_MULTIBYTE */
1904
if(vp->ed->hlist)
1905
{
1906
ed_histlist(vp->ed,n);
1907
pr_string(vp,Prompt);
1908
vp->ocur_virt = INVALID;
1909
ed_setcursor(vp->ed,physical,0,cur_phys,0);
1910
}
1911
else
1912
ed_ringbell();
1913
}
1914
else if(mode==INPUT && v<=1 && vp->ed->hlist)
1915
ed_histlist(vp->ed,0);
1916
#endif /* SHOPT_EDPREDICT */
1917
if( v<vp->ocur_virt || vp->ocur_virt==INVALID
1918
|| ( v==vp->ocur_virt
1919
&& (!is_print(virtual[v]) || !is_print(vp->o_v_char))) )
1920
{
1921
opflag = 0;
1922
p = 0;
1923
v = 0;
1924
}
1925
else
1926
{
1927
opflag = 1;
1928
p = vp->ocur_phys;
1929
v = vp->ocur_virt;
1930
if( !is_print(virtual[v]) )
1931
{
1932
/*** avoid double ^'s ***/
1933
++p;
1934
++v;
1935
}
1936
}
1937
virtual[last_virt+1] = 0;
1938
ncur_phys = ed_virt_to_phys(vp->ed,virtual,physical,cur_virt,v,p);
1939
p = genlen(physical);
1940
if( --p < 0 )
1941
last_phys = 0;
1942
else
1943
last_phys = p;
1944
1945
/*** see if this was a translate only ***/
1946
1947
if( mode == TRANSLATE )
1948
return;
1949
1950
/*** adjust left margin if necessary ***/
1951
1952
if( ncur_phys<first_w || ncur_phys>=(first_w + w_size) )
1953
{
1954
cursor(vp,first_w);
1955
first_w = ncur_phys - (w_size>>1);
1956
if( first_w < 0 )
1957
first_w = 0;
1958
vp->first_wind = cur_phys = first_w;
1959
}
1960
1961
/*** attempt to optimize search somewhat to find ***/
1962
/*** out where physical and window images differ ***/
1963
1964
if( first_w==vp->ofirst_wind && ncur_phys>=vp->ocur_phys && opflag==1 )
1965
{
1966
p = vp->ocur_phys;
1967
w = p - first_w;
1968
}
1969
else
1970
{
1971
p = first_w;
1972
w = 0;
1973
}
1974
1975
for(; (p<=last_phys && w<=vp->last_wind); ++p, ++w)
1976
{
1977
if( window[w] != physical[p] )
1978
break;
1979
}
1980
p_differ = p;
1981
1982
if( (p>last_phys || p>=first_w+w_size) && w>vp->last_wind
1983
&& cur_virt==vp->ocur_virt )
1984
{
1985
/*** images are identical ***/
1986
return;
1987
}
1988
1989
/*** copy the physical image to the window image ***/
1990
1991
if( last_virt != INVALID )
1992
{
1993
while( p <= last_phys && w < w_size )
1994
window[w++] = physical[p++];
1995
}
1996
new_lw = w;
1997
1998
/*** erase trailing characters if needed ***/
1999
2000
while( w <= vp->last_wind )
2001
window[w++] = ' ';
2002
vp->last_wind = --w;
2003
2004
p = p_differ;
2005
2006
/*** move cursor to start of difference ***/
2007
2008
cursor(vp,p);
2009
2010
/*** and output difference ***/
2011
2012
w = p - first_w;
2013
while( w <= vp->last_wind )
2014
putchar(window[w++]);
2015
2016
cur_phys = w + first_w;
2017
vp->last_wind = --new_lw;
2018
2019
if( last_phys >= w_size )
2020
{
2021
if( first_w == 0 )
2022
vp->long_char = '>';
2023
else if( last_phys < (first_w+w_size) )
2024
vp->long_char = '<';
2025
else
2026
vp->long_char = '*';
2027
}
2028
else
2029
vp->long_char = ' ';
2030
2031
if( vp->long_line != vp->long_char )
2032
{
2033
/*** indicate lines longer than window ***/
2034
while( w++ < w_size )
2035
{
2036
putchar(' ');
2037
++cur_phys;
2038
}
2039
putchar(vp->long_char);
2040
++cur_phys;
2041
vp->long_line = vp->long_char;
2042
}
2043
2044
if(vp->ed->e_multiline && vp->ofirst_wind==INVALID && !vp->ed->e_nocrnl)
2045
ed_setcursor(vp->ed, physical, last_phys+1, last_phys+1, -1);
2046
vp->ed->e_nocrnl = 0;
2047
vp->ocur_phys = ncur_phys;
2048
vp->ocur_virt = cur_virt;
2049
vp->ofirst_wind = first_w;
2050
2051
if( mode==INPUT && cur_virt>INVALID )
2052
++ncur_phys;
2053
2054
cursor(vp,ncur_phys);
2055
ed_flush(vp->ed);
2056
return;
2057
}
2058
2059
/*{ REPLACE( char, increment )
2060
*
2061
* Replace the cur_virt character with char. This routine attempts
2062
* to avoid using refresh().
2063
*
2064
* increment = 1, increment cur_virt after replacement.
2065
* = 0, leave cur_virt where it is.
2066
*
2067
}*/
2068
2069
static void replace(register Vi_t *vp, register int c, register int increment)
2070
{
2071
register int cur_window;
2072
2073
if( cur_virt == INVALID )
2074
{
2075
/*** can't replace invalid cursor ***/
2076
ed_ringbell();
2077
return;
2078
}
2079
cur_window = cur_phys - vp->first_wind;
2080
if( vp->ocur_virt == INVALID || !is_print(c)
2081
|| !is_print(virtual[cur_virt])
2082
|| !is_print(vp->o_v_char)
2083
#if SHOPT_MULTIBYTE
2084
|| !iswascii(c) || mbwidth(vp->o_v_char)>1
2085
|| !iswascii(virtual[cur_virt])
2086
#endif /* SHOPT_MULTIBYTE */
2087
|| (increment && (cur_window==w_size-1)
2088
|| !is_print(virtual[cur_virt+1])) )
2089
{
2090
/*** must use standard refresh routine ***/
2091
2092
cdelete(vp,1, BAD);
2093
append(vp,c, APPEND);
2094
if( increment && cur_virt<last_virt )
2095
++cur_virt;
2096
refresh(vp,CONTROL);
2097
}
2098
else
2099
{
2100
virtual[cur_virt] = c;
2101
physical[cur_phys] = c;
2102
window[cur_window] = c;
2103
putchar(c);
2104
if(increment)
2105
{
2106
c = virtual[++cur_virt];
2107
++cur_phys;
2108
}
2109
else
2110
{
2111
putchar('\b');
2112
}
2113
vp->o_v_char = c;
2114
ed_flush(vp->ed);
2115
}
2116
return;
2117
}
2118
2119
/*{ RESTORE_V()
2120
*
2121
* Restore the contents of virtual space from u_space.
2122
*
2123
}*/
2124
2125
static void restore_v(register Vi_t *vp)
2126
{
2127
register int tmpcol;
2128
genchar tmpspace[MAXLINE];
2129
2130
if( vp->u_column == INVALID-1 )
2131
{
2132
/*** never saved anything ***/
2133
ed_ringbell();
2134
return;
2135
}
2136
gencpy(tmpspace, vp->u_space);
2137
tmpcol = vp->u_column;
2138
save_v(vp);
2139
gencpy(virtual, tmpspace);
2140
cur_virt = tmpcol;
2141
last_virt = genlen(tmpspace) - 1;
2142
vp->ocur_virt = MAXCHAR; /** invalidate refresh optimization **/
2143
return;
2144
}
2145
2146
/*{ SAVE_LAST()
2147
*
2148
* If the user has typed something, save it in last line.
2149
*
2150
}*/
2151
2152
static void save_last(register Vi_t* vp)
2153
{
2154
register int i;
2155
2156
if( (i = cur_virt - first_virt + 1) > 0 )
2157
{
2158
/*** save last thing user typed ***/
2159
if(i >= MAXLINE)
2160
i = MAXLINE-1;
2161
genncpy(vp->lastline, (&virtual[first_virt]), i);
2162
vp->lastline[i] = '\0';
2163
}
2164
return;
2165
}
2166
2167
/*{ SAVE_V()
2168
*
2169
* This routine will save the contents of virtual in u_space.
2170
*
2171
}*/
2172
2173
static void save_v(register Vi_t *vp)
2174
{
2175
if(!inmacro)
2176
{
2177
virtual[last_virt + 1] = '\0';
2178
gencpy(vp->u_space, virtual);
2179
vp->u_column = cur_virt;
2180
}
2181
return;
2182
}
2183
2184
/*{ SEARCH( mode )
2185
*
2186
* Search history file for regular expression.
2187
*
2188
* mode = '/' require search string and search new to old
2189
* mode = '?' require search string and search old to new
2190
* mode = 'N' repeat last search in reverse direction
2191
* mode = 'n' repeat last search
2192
*
2193
}*/
2194
2195
/*
2196
* search for <string> in the current command
2197
*/
2198
static int curline_search(Vi_t *vp, const char *string)
2199
{
2200
register size_t len=strlen(string);
2201
register const char *dp,*cp=string, *dpmax;
2202
#if SHOPT_MULTIBYTE
2203
ed_external(vp->u_space,(char*)vp->u_space);
2204
#endif /* SHOPT_MULTIBYTE */
2205
for(dp=(char*)vp->u_space,dpmax=dp+strlen(dp)-len; dp<=dpmax; dp++)
2206
{
2207
if(*dp==*cp && memcmp(cp,dp,len)==0)
2208
return(dp-(char*)vp->u_space);
2209
}
2210
#if SHOPT_MULTIBYTE
2211
ed_internal((char*)vp->u_space,vp->u_space);
2212
#endif /* SHOPT_MULTIBYTE */
2213
return(-1);
2214
}
2215
2216
static int search(register Vi_t* vp,register int mode)
2217
{
2218
register int new_direction;
2219
register int oldcurhline;
2220
register int i;
2221
Histloc_t location;
2222
2223
if( vp->direction == -2 && mode != 'n')
2224
vp->direction = -1;
2225
if( mode == '/' || mode == '?')
2226
{
2227
/*** new search expression ***/
2228
del_line(vp,BAD);
2229
append(vp,mode, APPEND);
2230
refresh(vp,INPUT);
2231
first_virt = 1;
2232
getline(vp,SEARCH);
2233
first_virt = 0;
2234
virtual[last_virt + 1] = '\0'; /*** make null terminated ***/
2235
vp->direction = mode=='/' ? -1 : 1;
2236
}
2237
2238
if( cur_virt == INVALID )
2239
{
2240
/*** no operation ***/
2241
return(ABORT);
2242
}
2243
2244
if( cur_virt==0 || fold(mode)=='N' )
2245
{
2246
/*** user wants repeat of last search ***/
2247
del_line(vp,BAD);
2248
strcpy( ((char*)virtual)+1, lsearch);
2249
#if SHOPT_MULTIBYTE
2250
*((char*)virtual) = '/';
2251
ed_internal((char*)virtual,virtual);
2252
#endif /* SHOPT_MULTIBYTE */
2253
}
2254
2255
if( mode == 'N' )
2256
new_direction = -vp->direction;
2257
else
2258
new_direction = vp->direction;
2259
2260
2261
/*** now search ***/
2262
2263
oldcurhline = curhline;
2264
#if SHOPT_MULTIBYTE
2265
ed_external(virtual,(char*)virtual);
2266
#endif /* SHOPT_MULTIBYTE */
2267
if(mode=='?' && (i=curline_search(vp,((char*)virtual)+1))>=0)
2268
{
2269
location.hist_command = curhline;
2270
location.hist_char = i;
2271
}
2272
else
2273
{
2274
i = INVALID;
2275
if( new_direction==1 && curhline >= histmax )
2276
curhline = histmin + 1;
2277
location = hist_find(shgd->hist_ptr,((char*)virtual)+1, curhline, 1, new_direction);
2278
}
2279
cur_virt = i;
2280
strncpy(lsearch, ((char*)virtual)+1, SEARCHSIZE);
2281
lsearch[SEARCHSIZE-1] = 0;
2282
if( (curhline=location.hist_command) >=0 )
2283
{
2284
vp->ocur_virt = INVALID;
2285
return(GOOD);
2286
}
2287
2288
/*** could not find matching line ***/
2289
2290
curhline = oldcurhline;
2291
return(BAD);
2292
}
2293
2294
/*{ SYNC_CURSOR()
2295
*
2296
* This routine will move the physical cursor to the same
2297
* column as the virtual cursor.
2298
*
2299
}*/
2300
2301
static void sync_cursor(register Vi_t *vp)
2302
{
2303
register int p;
2304
register int v;
2305
register int c;
2306
int new_phys;
2307
2308
if( cur_virt == INVALID )
2309
return;
2310
2311
/*** find physical col that corresponds to virtual col ***/
2312
2313
new_phys = 0;
2314
if(vp->first_wind==vp->ofirst_wind && cur_virt>vp->ocur_virt && vp->ocur_virt!=INVALID)
2315
{
2316
/*** try to optimize search a little ***/
2317
p = vp->ocur_phys + 1;
2318
#if SHOPT_MULTIBYTE
2319
while(physical[p]==MARKER)
2320
p++;
2321
#endif /* SHOPT_MULTIBYTE */
2322
v = vp->ocur_virt + 1;
2323
}
2324
else
2325
{
2326
p = 0;
2327
v = 0;
2328
}
2329
for(; v <= last_virt; ++p, ++v)
2330
{
2331
#if SHOPT_MULTIBYTE
2332
int d;
2333
c = virtual[v];
2334
if((d = mbwidth(c)) > 1)
2335
{
2336
if( v != cur_virt )
2337
p += (d-1);
2338
}
2339
else if(!iswprint(c))
2340
#else
2341
c = virtual[v];
2342
if(!isprint(c))
2343
#endif /* SHOPT_MULTIBYTE */
2344
{
2345
if( c == '\t' )
2346
{
2347
p -= ((p+editb.e_plen)%TABSIZE);
2348
p += (TABSIZE-1);
2349
}
2350
else
2351
{
2352
++p;
2353
}
2354
}
2355
if( v == cur_virt )
2356
{
2357
new_phys = p;
2358
break;
2359
}
2360
}
2361
2362
if( new_phys < vp->first_wind || new_phys >= vp->first_wind + w_size )
2363
{
2364
/*** asked to move outside of window ***/
2365
2366
window[0] = '\0';
2367
refresh(vp,CONTROL);
2368
return;
2369
}
2370
2371
cursor(vp,new_phys);
2372
ed_flush(vp->ed);
2373
vp->ocur_phys = cur_phys;
2374
vp->ocur_virt = cur_virt;
2375
vp->o_v_char = virtual[vp->ocur_virt];
2376
2377
return;
2378
}
2379
2380
/*{ TEXTMOD( command, mode )
2381
*
2382
* Modify text operations.
2383
*
2384
* mode != 0, repeat previous operation
2385
*
2386
}*/
2387
2388
static int textmod(register Vi_t *vp,register int c, int mode)
2389
{
2390
register int i;
2391
register genchar *p = vp->lastline;
2392
register int trepeat = vp->repeat;
2393
genchar *savep;
2394
2395
if(mode && (fold(vp->lastmotion)=='F' || fold(vp->lastmotion)=='T'))
2396
vp->lastmotion = ';';
2397
2398
if( fold(c) == 'P' )
2399
{
2400
/*** change p from lastline to yankbuf ***/
2401
p = yankbuf;
2402
}
2403
2404
addin:
2405
switch( c )
2406
{
2407
/***** Input commands *****/
2408
2409
#if KSHELL
2410
case '\t':
2411
if(vp->ed->e_tabcount!=1)
2412
return(BAD);
2413
c = '=';
2414
case '*': /** do file name expansion in place **/
2415
case '\\': /** do file name completion in place **/
2416
if( cur_virt == INVALID )
2417
return(BAD);
2418
case '=': /** list file name expansions **/
2419
save_v(vp);
2420
i = last_virt;
2421
++last_virt;
2422
mode = cur_virt-1;
2423
virtual[last_virt] = 0;
2424
if(ed_expand(vp->ed,(char*)virtual, &cur_virt, &last_virt, c, vp->repeat_set?vp->repeat:-1)<0)
2425
{
2426
if(vp->ed->e_tabcount)
2427
{
2428
vp->ed->e_tabcount=2;
2429
ed_ungetchar(vp->ed,'\t');
2430
--last_virt;
2431
return(APPEND);
2432
}
2433
last_virt = i;
2434
ed_ringbell();
2435
}
2436
else if((c=='=' || (c=='\\'&&virtual[i]=='/')) && !vp->repeat_set)
2437
{
2438
last_virt = i;
2439
vp->nonewline++;
2440
ed_ungetchar(vp->ed,cntl('L'));
2441
return(GOOD);
2442
}
2443
else
2444
{
2445
--cur_virt;
2446
--last_virt;
2447
vp->ocur_virt = MAXCHAR;
2448
if(c=='=' || (mode<cur_virt && (virtual[cur_virt]==' ' || virtual[cur_virt]=='/')))
2449
vp->ed->e_tabcount = 0;
2450
return(APPEND);
2451
}
2452
break;
2453
2454
case '@': /** macro expansion **/
2455
if( mode )
2456
c = vp->lastmacro;
2457
else
2458
if((c=getrchar(vp))==ESC)
2459
return(GOOD);
2460
if(!inmacro)
2461
vp->lastmacro = c;
2462
if(ed_macro(vp->ed,c))
2463
{
2464
save_v(vp);
2465
inmacro++;
2466
return(GOOD);
2467
}
2468
ed_ringbell();
2469
return(BAD);
2470
2471
#endif /* KSHELL */
2472
case '_': /** append last argument of prev command **/
2473
save_v(vp);
2474
{
2475
genchar tmpbuf[MAXLINE];
2476
if(vp->repeat_set==0)
2477
vp->repeat = -1;
2478
p = (genchar*)hist_word((char*)tmpbuf,MAXLINE,vp->repeat);
2479
if(p==0)
2480
{
2481
ed_ringbell();
2482
break;
2483
}
2484
#if SHOPT_MULTIBYTE
2485
ed_internal((char*)p,tmpbuf);
2486
p = tmpbuf;
2487
#endif /* SHOPT_MULTIBYTE */
2488
i = ' ';
2489
do
2490
{
2491
append(vp,i,APPEND);
2492
}
2493
while(i = *p++);
2494
return(APPEND);
2495
}
2496
2497
case 'A': /** append to end of line **/
2498
cur_virt = last_virt;
2499
sync_cursor(vp);
2500
2501
case 'a': /** append **/
2502
if( fold(mode) == 'A' )
2503
{
2504
c = 'p';
2505
goto addin;
2506
}
2507
save_v(vp);
2508
if( cur_virt != INVALID )
2509
{
2510
first_virt = cur_virt + 1;
2511
cursor(vp,cur_phys + 1);
2512
ed_flush(vp->ed);
2513
}
2514
return(APPEND);
2515
2516
case 'I': /** insert at beginning of line **/
2517
cur_virt = first_virt;
2518
sync_cursor(vp);
2519
2520
case 'i': /** insert **/
2521
if( fold(mode) == 'I' )
2522
{
2523
c = 'P';
2524
goto addin;
2525
}
2526
save_v(vp);
2527
if( cur_virt != INVALID )
2528
{
2529
vp->o_v_char = virtual[cur_virt];
2530
first_virt = cur_virt--;
2531
}
2532
return(INSERT);
2533
2534
case 'C': /** change to eol **/
2535
c = '$';
2536
goto chgeol;
2537
2538
case 'c': /** change **/
2539
if( mode )
2540
c = vp->lastmotion;
2541
else
2542
c = getcount(vp,ed_getchar(vp->ed,-1));
2543
chgeol:
2544
vp->lastmotion = c;
2545
if( c == 'c' )
2546
{
2547
del_line(vp,GOOD);
2548
return(APPEND);
2549
}
2550
2551
if(!delmotion(vp, c, 'c'))
2552
return(BAD);
2553
2554
if( mode == 'c' )
2555
{
2556
c = 'p';
2557
trepeat = 1;
2558
goto addin;
2559
}
2560
first_virt = cur_virt + 1;
2561
return(APPEND);
2562
2563
case 'D': /** delete to eol **/
2564
c = '$';
2565
goto deleol;
2566
2567
case 'd': /** delete **/
2568
if( mode )
2569
c = vp->lastmotion;
2570
else
2571
c = getcount(vp,ed_getchar(vp->ed,-1));
2572
deleol:
2573
vp->lastmotion = c;
2574
if( c == 'd' )
2575
{
2576
del_line(vp,GOOD);
2577
break;
2578
}
2579
if(!delmotion(vp, c, 'd'))
2580
return(BAD);
2581
if( cur_virt < last_virt )
2582
++cur_virt;
2583
break;
2584
2585
case 'P':
2586
if( p[0] == '\0' )
2587
return(BAD);
2588
if( cur_virt != INVALID )
2589
{
2590
i = virtual[cur_virt];
2591
if(!is_print(i))
2592
vp->ocur_virt = INVALID;
2593
--cur_virt;
2594
}
2595
2596
case 'p': /** print **/
2597
if( p[0] == '\0' )
2598
return(BAD);
2599
2600
if( mode != 's' && mode != 'c' )
2601
{
2602
save_v(vp);
2603
if( c == 'P' )
2604
{
2605
/*** fix stored cur_virt ***/
2606
++vp->u_column;
2607
}
2608
}
2609
if( mode == 'R' )
2610
mode = REPLACE;
2611
else
2612
mode = APPEND;
2613
savep = p;
2614
for(i=0; i<trepeat; ++i)
2615
{
2616
while(c= *p++)
2617
append(vp,c,mode);
2618
p = savep;
2619
}
2620
break;
2621
2622
case 'R': /* Replace many chars **/
2623
if( mode == 'R' )
2624
{
2625
c = 'P';
2626
goto addin;
2627
}
2628
save_v(vp);
2629
if( cur_virt != INVALID )
2630
first_virt = cur_virt;
2631
return(REPLACE);
2632
2633
case 'r': /** replace **/
2634
if( mode )
2635
c = *p;
2636
else
2637
if((c=getrchar(vp))==ESC)
2638
return(GOOD);
2639
*p = c;
2640
save_v(vp);
2641
while(trepeat--)
2642
replace(vp,c, trepeat!=0);
2643
return(GOOD);
2644
2645
case 'S': /** Substitute line - cc **/
2646
c = 'c';
2647
goto chgeol;
2648
2649
case 's': /** substitute **/
2650
save_v(vp);
2651
cdelete(vp,vp->repeat, BAD);
2652
if( mode )
2653
{
2654
c = 'p';
2655
trepeat = 1;
2656
goto addin;
2657
}
2658
first_virt = cur_virt + 1;
2659
return(APPEND);
2660
2661
case 'Y': /** Yank to end of line **/
2662
c = '$';
2663
goto yankeol;
2664
2665
case 'y': /** yank thru motion **/
2666
if( mode )
2667
c = vp->lastmotion;
2668
else
2669
c = getcount(vp,ed_getchar(vp->ed,-1));
2670
yankeol:
2671
vp->lastmotion = c;
2672
if( c == 'y' )
2673
{
2674
gencpy(yankbuf, virtual);
2675
}
2676
else if(!delmotion(vp, c, 'y'))
2677
{
2678
return(BAD);
2679
}
2680
break;
2681
2682
case 'x': /** delete repeat chars forward - dl **/
2683
c = 'l';
2684
goto deleol;
2685
2686
case 'X': /** delete repeat chars backward - dh **/
2687
c = 'h';
2688
goto deleol;
2689
2690
case '~': /** invert case and advance **/
2691
if( cur_virt != INVALID )
2692
{
2693
save_v(vp);
2694
i = INVALID;
2695
while(trepeat-->0 && i!=cur_virt)
2696
{
2697
i = cur_virt;
2698
c = virtual[cur_virt];
2699
#if SHOPT_MULTIBYTE
2700
if((c&~STRIP)==0)
2701
#endif /* SHOPT_MULTIBYTE */
2702
if( isupper(c) )
2703
c = tolower(c);
2704
else if( islower(c) )
2705
c = toupper(c);
2706
replace(vp,c, 1);
2707
}
2708
return(GOOD);
2709
}
2710
else
2711
return(BAD);
2712
2713
default:
2714
return(BAD);
2715
}
2716
refresh(vp,CONTROL);
2717
return(GOOD);
2718
}
2719
2720
2721
#if SHOPT_MULTIBYTE
2722
static int _isalph(register int v)
2723
{
2724
#ifdef _lib_iswalnum
2725
return(iswalnum(v) || v=='_');
2726
#else
2727
return((v&~STRIP) || isalnum(v) || v=='_');
2728
#endif
2729
}
2730
2731
2732
static int _isblank(register int v)
2733
{
2734
return((v&~STRIP)==0 && isspace(v));
2735
}
2736
2737
static int _ismetach(register int v)
2738
{
2739
return((v&~STRIP)==0 && ismeta(v));
2740
}
2741
2742
#endif /* SHOPT_MULTIBYTE */
2743
2744
/*
2745
* get a character, after ^V processing
2746
*/
2747
static int getrchar(register Vi_t *vp)
2748
{
2749
register int c;
2750
if((c=ed_getchar(vp->ed,1))== usrlnext)
2751
c = ed_getchar(vp->ed,2);
2752
return(c);
2753
}
2754
2755