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