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