Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/ksh93/edit/emacs.c
1810 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1982-2012 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* David Korn <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/* Original version by Michael T. Veach
22
* Adapted for ksh by David Korn */
23
/* EMACS_MODES: c tabstop=4
24
25
One line screen editor for any program
26
27
*/
28
29
30
/* The following is provided by:
31
*
32
* Matthijs N. Melchior
33
* AT&T Network Systems International
34
* APT Nederland
35
* HV BZ335 x2962
36
* hvlpb!mmelchio
37
*
38
* These are now on by default
39
*
40
* ESH_NFIRST
41
* - A ^N as first history related command after the prompt will move
42
* to the next command relative to the last known history position.
43
* It will not start at the position where the last command was entered
44
* as is done by the ^P command. Every history related command will
45
* set both the current and last position. Executing a command will
46
* only set the current position.
47
*
48
* ESH_KAPPEND
49
* - Successive kill and delete commands will accumulate their data
50
* in the kill buffer, by appending or prepending as appropriate.
51
* This mode will be reset by any command not adding something to the
52
* kill buffer.
53
*
54
* ESH_BETTER
55
* - Some enhancements:
56
* - argument for a macro is passed to its replacement
57
* - ^X^H command to find out about history position (debugging)
58
* - ^X^D command to show any debugging info
59
*
60
* I do not pretend these for changes are completely independent,
61
* but you can use them to seperate features.
62
*/
63
64
#include <ast.h>
65
#include "FEATURE/cmds"
66
#if KSHELL
67
# include "defs.h"
68
#else
69
# include <ctype.h>
70
#endif /* KSHELL */
71
#include "io.h"
72
73
#include "history.h"
74
#include "edit.h"
75
#include "terminal.h"
76
77
#define ESH_NFIRST
78
#define ESH_KAPPEND
79
#define ESH_BETTER
80
81
#undef putchar
82
#define putchar(ed,c) ed_putchar(ed,c)
83
#define beep() ed_ringbell()
84
85
86
#if SHOPT_MULTIBYTE
87
# define gencpy(a,b) ed_gencpy(a,b)
88
# define genncpy(a,b,n) ed_genncpy(a,b,n)
89
# define genlen(str) ed_genlen(str)
90
static int print(int);
91
static int _isword(int);
92
# define isword(c) _isword(out[c])
93
94
#else
95
# define gencpy(a,b) strcpy((char*)(a),(char*)(b))
96
# define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n)
97
# define genlen(str) strlen(str)
98
# define print(c) isprint(c)
99
# define isword(c) (isalnum(out[c]) || (out[c]=='_'))
100
#endif /*SHOPT_MULTIBYTE */
101
102
typedef struct _emacs_
103
{
104
genchar *screen; /* pointer to window buffer */
105
genchar *cursor; /* Cursor in real screen */
106
int mark;
107
int in_mult;
108
char cr_ok;
109
char CntrlO;
110
char overflow; /* Screen overflow flag set */
111
char scvalid; /* Screen is up to date */
112
char lastdraw; /* last update type */
113
int offset; /* Screen offset */
114
enum
115
{
116
CRT=0, /* Crt terminal */
117
PAPER /* Paper terminal */
118
} terminal;
119
Histloc_t _location;
120
int prevdirection;
121
Edit_t *ed; /* pointer to edit data */
122
} Emacs_t;
123
124
#define editb (*ep->ed)
125
#define eol editb.e_eol
126
#define cur editb.e_cur
127
#define hline editb.e_hline
128
#define hloff editb.e_hloff
129
#define hismin editb.e_hismin
130
#define usrkill editb.e_kill
131
#define usrlnext editb.e_lnext
132
#define usreof editb.e_eof
133
#define usrerase editb.e_erase
134
#define crallowed editb.e_crlf
135
#define Prompt editb.e_prompt
136
#define plen editb.e_plen
137
#define kstack editb.e_killbuf
138
#define lstring editb.e_search
139
#define lookahead editb.e_lookahead
140
#define env editb.e_env
141
#define raw editb.e_raw
142
#define histlines editb.e_hismax
143
#define w_size editb.e_wsize
144
#define drawbuff editb.e_inbuf
145
#define killing editb.e_mode
146
#define location ep->_location
147
148
#define LBUF 100
149
#define KILLCHAR UKILL
150
#define ERASECHAR UERASE
151
#define EOFCHAR UEOF
152
#define LNEXTCHAR ULNEXT
153
#define DELETE ('a'==97?0177:7)
154
155
/**********************
156
A large lookahead helps when the user is inserting
157
characters in the middle of the line.
158
************************/
159
160
161
typedef enum
162
{
163
FIRST, /* First time thru for logical line, prompt on screen */
164
REFRESH, /* Redraw entire screen */
165
APPEND, /* Append char before cursor to screen */
166
UPDATE, /* Update the screen as need be */
167
FINAL /* Update screen even if pending look ahead */
168
} Draw_t;
169
170
static void draw(Emacs_t*,Draw_t);
171
static int escape(Emacs_t*,genchar*, int);
172
static void putstring(Emacs_t*,char*);
173
static void search(Emacs_t*,genchar*,int);
174
static void setcursor(Emacs_t*,int, int);
175
static void show_info(Emacs_t*,const char*);
176
static void xcommands(Emacs_t*,int);
177
178
int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
179
{
180
Edit_t *ed = (Edit_t*)context;
181
register int c;
182
register int i;
183
register genchar *out;
184
register int count;
185
register Emacs_t *ep = ed->e_emacs;
186
int adjust,oadjust;
187
char backslash;
188
genchar *kptr;
189
char prompt[PRSIZE];
190
genchar Screen[MAXLINE];
191
memset(Screen,0,sizeof(Screen));
192
if(!ep)
193
{
194
ep = ed->e_emacs = newof(0,Emacs_t,1,0);
195
ep->ed = ed;
196
ep->prevdirection = 1;
197
location.hist_command = -5;
198
}
199
Prompt = prompt;
200
ep->screen = Screen;
201
ep->lastdraw = FINAL;
202
if(tty_raw(ERRIO,0) < 0)
203
{
204
return(reedit?reedit:ed_read(context, fd,buff,scend,0));
205
}
206
raw = 1;
207
/* This mess in case the read system call fails */
208
209
ed_setup(ep->ed,fd,reedit);
210
out = (genchar*)buff;
211
#if SHOPT_MULTIBYTE
212
out = (genchar*)roundof(buff-(char*)0,sizeof(genchar));
213
if(reedit)
214
ed_internal(buff,out);
215
#endif /* SHOPT_MULTIBYTE */
216
if(!kstack)
217
{
218
kstack = (genchar*)malloc(CHARSIZE*MAXLINE);
219
kstack[0] = '\0';
220
}
221
drawbuff = out;
222
#ifdef ESH_NFIRST
223
if (location.hist_command == -5) /* to be initialized */
224
{
225
kstack[0] = '\0'; /* also clear kstack... */
226
location.hist_command = hline;
227
location.hist_line = hloff;
228
}
229
if (location.hist_command <= hismin) /* don't start below minimum */
230
{
231
location.hist_command = hismin + 1;
232
location.hist_line = 0;
233
}
234
ep->in_mult = hloff; /* save pos in last command */
235
#endif /* ESH_NFIRST */
236
i = sigsetjmp(env,0);
237
if (i !=0)
238
{
239
if(ep->ed->e_multiline)
240
{
241
cur = eol;
242
draw(ep,FINAL);
243
ed_flush(ep->ed);
244
}
245
tty_cooked(ERRIO);
246
if (i == UEOF)
247
{
248
return(0); /* EOF */
249
}
250
return(-1); /* some other error */
251
}
252
out[reedit] = 0;
253
if(scend+plen > (MAXLINE-2))
254
scend = (MAXLINE-2)-plen;
255
ep->mark = 0;
256
cur = eol;
257
draw(ep,reedit?REFRESH:FIRST);
258
adjust = -1;
259
backslash = 0;
260
if (ep->CntrlO)
261
{
262
#ifdef ESH_NFIRST
263
ed_ungetchar(ep->ed,cntl('N'));
264
#else
265
location = hist_locate(shgd->hist_ptr,location.hist_command,location.hist_line,1);
266
if (location.hist_command < histlines)
267
{
268
hline = location.hist_command;
269
hloff = location.hist_line;
270
hist_copy((char*)kstack,MAXLINE, hline,hloff);
271
# if SHOPT_MULTIBYTE
272
ed_internal((char*)kstack,kstack);
273
# endif /* SHOPT_MULTIBYTE */
274
ed_ungetchar(ep->ed,cntl('Y'));
275
}
276
#endif /* ESH_NFIRST */
277
}
278
ep->CntrlO = 0;
279
while ((c = ed_getchar(ep->ed,0)) != (-1))
280
{
281
if (backslash)
282
{
283
backslash = 0;
284
if (c==usrerase||c==usrkill||(!print(c) &&
285
(c!='\r'&&c!='\n')))
286
{
287
/* accept a backslashed character */
288
cur--;
289
out[cur++] = c;
290
out[eol] = '\0';
291
draw(ep,APPEND);
292
continue;
293
}
294
}
295
if (c == usrkill)
296
{
297
c = KILLCHAR ;
298
}
299
else if (c == usrerase)
300
{
301
c = ERASECHAR ;
302
}
303
else if (c == usrlnext)
304
{
305
c = LNEXTCHAR ;
306
}
307
else if ((c == usreof)&&(eol == 0))
308
{
309
c = EOFCHAR;
310
}
311
#ifdef ESH_KAPPEND
312
if (--killing <= 0) /* reset killing flag */
313
killing = 0;
314
#endif
315
oadjust = count = adjust;
316
if(count<0)
317
count = 1;
318
adjust = -1;
319
i = cur;
320
if(c!='\t' && c!=ESC && !isdigit(c))
321
ep->ed->e_tabcount = 0;
322
switch(c)
323
{
324
case LNEXTCHAR:
325
c = ed_getchar(ep->ed,2);
326
goto do_default_processing;
327
case cntl('V'):
328
show_info(ep,fmtident(e_version));
329
continue;
330
case '\0':
331
ep->mark = i;
332
continue;
333
case cntl('X'):
334
xcommands(ep,count);
335
continue;
336
case EOFCHAR:
337
ed_flush(ep->ed);
338
tty_cooked(ERRIO);
339
return(0);
340
#ifdef u370
341
case cntl('S') :
342
case cntl('Q') :
343
continue;
344
#endif /* u370 */
345
case '\t':
346
if(cur>0 && ep->ed->sh->nextprompt)
347
{
348
if(ep->ed->e_tabcount==0)
349
{
350
ep->ed->e_tabcount=1;
351
ed_ungetchar(ep->ed,ESC);
352
goto do_escape;
353
}
354
else if(ep->ed->e_tabcount==1)
355
{
356
ed_ungetchar(ep->ed,'=');
357
goto do_escape;
358
}
359
ep->ed->e_tabcount = 0;
360
}
361
do_default_processing:
362
default:
363
364
if ((eol+1) >= (scend)) /* will not fit on line */
365
{
366
ed_ungetchar(ep->ed,c); /* save character for next line */
367
goto process;
368
}
369
for(i= ++eol; i>cur; i--)
370
out[i] = out[i-1];
371
backslash = (c == '\\');
372
out[cur++] = c;
373
draw(ep,APPEND);
374
continue;
375
case cntl('Y') :
376
{
377
c = genlen(kstack);
378
if ((c + eol) > scend)
379
{
380
beep();
381
continue;
382
}
383
ep->mark = i;
384
for(i=eol;i>=cur;i--)
385
out[c+i] = out[i];
386
kptr=kstack;
387
while (i = *kptr++)
388
out[cur++] = i;
389
draw(ep,UPDATE);
390
eol = genlen(out);
391
continue;
392
}
393
case '\n':
394
case '\r':
395
c = '\n';
396
goto process;
397
398
case DELETE: /* delete char 0x7f */
399
case '\b': /* backspace, ^h */
400
case ERASECHAR :
401
if (count > i)
402
count = i;
403
#ifdef ESH_KAPPEND
404
kptr = &kstack[count]; /* move old contents here */
405
if (killing) /* prepend to killbuf */
406
{
407
c = genlen(kstack) + CHARSIZE; /* include '\0' */
408
while(c--) /* copy stuff */
409
kptr[c] = kstack[c];
410
}
411
else
412
*kptr = 0; /* this is end of data */
413
killing = 2; /* we are killing */
414
i -= count;
415
eol -= count;
416
genncpy(kstack,out+i,cur-i);
417
#else
418
while ((count--)&&(i>0))
419
{
420
i--;
421
eol--;
422
}
423
genncpy(kstack,out+i,cur-i);
424
kstack[cur-i] = 0;
425
#endif /* ESH_KAPPEND */
426
gencpy(out+i,out+cur);
427
ep->mark = i;
428
goto update;
429
case cntl('W') :
430
#ifdef ESH_KAPPEND
431
++killing; /* keep killing flag */
432
#endif
433
if (ep->mark > eol )
434
ep->mark = eol;
435
if (ep->mark == i)
436
continue;
437
if (ep->mark > i)
438
{
439
adjust = ep->mark - i;
440
ed_ungetchar(ep->ed,cntl('D'));
441
continue;
442
}
443
adjust = i - ep->mark;
444
ed_ungetchar(ep->ed,usrerase);
445
continue;
446
case cntl('D') :
447
ep->mark = i;
448
#ifdef ESH_KAPPEND
449
if (killing)
450
kptr = &kstack[genlen(kstack)]; /* append here */
451
else
452
kptr = kstack;
453
killing = 2; /* we are now killing */
454
#else
455
kptr = kstack;
456
#endif /* ESH_KAPPEND */
457
while ((count--)&&(eol>0)&&(i<eol))
458
{
459
*kptr++ = out[i];
460
eol--;
461
while(1)
462
{
463
if ((out[i] = out[(i+1)])==0)
464
break;
465
i++;
466
}
467
i = cur;
468
}
469
*kptr = '\0';
470
goto update;
471
case cntl('C') :
472
case cntl('F') :
473
{
474
int cntlC = (c==cntl('C'));
475
while (count-- && eol>i)
476
{
477
if (cntlC)
478
{
479
c = out[i];
480
#if SHOPT_MULTIBYTE
481
if((c&~STRIP)==0 && islower(c))
482
#else
483
if(islower(c))
484
#endif /* SHOPT_MULTIBYTE */
485
{
486
c += 'A' - 'a';
487
out[i] = c;
488
}
489
}
490
i++;
491
}
492
goto update;
493
}
494
case cntl(']') :
495
c = ed_getchar(ep->ed,1);
496
if ((count == 0) || (count > eol))
497
{
498
beep();
499
continue;
500
}
501
if (out[i])
502
i++;
503
while (i < eol)
504
{
505
if (out[i] == c && --count==0)
506
goto update;
507
i++;
508
}
509
i = 0;
510
while (i < cur)
511
{
512
if (out[i] == c && --count==0)
513
break;
514
i++;
515
};
516
517
update:
518
cur = i;
519
draw(ep,UPDATE);
520
continue;
521
522
case cntl('B') :
523
if (count > i)
524
count = i;
525
i -= count;
526
goto update;
527
case cntl('T') :
528
if ((sh_isoption(SH_EMACS))&& (eol!=i))
529
i++;
530
if (i >= 2)
531
{
532
c = out[i - 1];
533
out[i-1] = out[i-2];
534
out[i-2] = c;
535
}
536
else
537
{
538
if(sh_isoption(SH_EMACS))
539
i--;
540
beep();
541
continue;
542
}
543
goto update;
544
case cntl('A') :
545
i = 0;
546
goto update;
547
case cntl('E') :
548
i = eol;
549
goto update;
550
case cntl('U') :
551
adjust = 4*count;
552
continue;
553
case KILLCHAR :
554
cur = 0;
555
oadjust = -1;
556
case cntl('K') :
557
if(oadjust >= 0)
558
{
559
#ifdef ESH_KAPPEND
560
killing = 2; /* set killing signal */
561
#endif
562
ep->mark = count;
563
ed_ungetchar(ep->ed,cntl('W'));
564
continue;
565
}
566
i = cur;
567
eol = i;
568
ep->mark = i;
569
#ifdef ESH_KAPPEND
570
if (killing) /* append to kill buffer */
571
gencpy(&kstack[genlen(kstack)], &out[i]);
572
else
573
gencpy(kstack,&out[i]);
574
killing = 2; /* set killing signal */
575
#else
576
gencpy(kstack,&out[i]);
577
#endif /* ESH_KAPPEND */
578
out[i] = 0;
579
draw(ep,UPDATE);
580
if (c == KILLCHAR)
581
{
582
if (ep->terminal == PAPER)
583
{
584
putchar(ep->ed,'\n');
585
putstring(ep,Prompt);
586
}
587
c = ed_getchar(ep->ed,0);
588
if (c != usrkill)
589
{
590
ed_ungetchar(ep->ed,c);
591
continue;
592
}
593
if (ep->terminal == PAPER)
594
ep->terminal = CRT;
595
else
596
{
597
ep->terminal = PAPER;
598
putchar(ep->ed,'\n');
599
putstring(ep,Prompt);
600
}
601
}
602
continue;
603
case cntl('L'):
604
if(!ep->ed->e_nocrnl)
605
ed_crlf(ep->ed);
606
draw(ep,REFRESH);
607
ep->ed->e_nocrnl = 0;
608
continue;
609
case cntl('[') :
610
do_escape:
611
adjust = escape(ep,out,oadjust);
612
continue;
613
case cntl('R') :
614
search(ep,out,count);
615
goto drawline;
616
case cntl('P') :
617
#if SHOPT_EDPREDICT
618
if(ep->ed->hlist)
619
{
620
if(ep->ed->hoff == 0)
621
{
622
beep();
623
continue;
624
}
625
ep->ed->hoff--;
626
goto hupdate;
627
}
628
#endif /* SHOPT_EDPREDICT */
629
if (count <= hloff)
630
hloff -= count;
631
else
632
{
633
hline -= count - hloff;
634
hloff = 0;
635
}
636
#ifdef ESH_NFIRST
637
if (hline <= hismin)
638
#else
639
if (hline < hismin)
640
#endif /* ESH_NFIRST */
641
{
642
hline = hismin+1;
643
beep();
644
#ifndef ESH_NFIRST
645
continue;
646
#endif
647
}
648
goto common;
649
650
case cntl('O') :
651
location.hist_command = hline;
652
location.hist_line = hloff;
653
ep->CntrlO = 1;
654
c = '\n';
655
goto process;
656
case cntl('N') :
657
#if SHOPT_EDPREDICT
658
if(ep->ed->hlist)
659
{
660
if(ep->ed->hoff >= ep->ed->hmax)
661
{
662
beep();
663
continue;
664
}
665
ep->ed->hoff++;
666
hupdate:
667
ed_histlist(ep->ed,*ep->ed->hlist!=0);
668
draw(ep,REFRESH);
669
continue;
670
}
671
#endif /* SHOPT_EDPREDICT */
672
#ifdef ESH_NFIRST
673
hline = location.hist_command; /* start at saved position */
674
hloff = location.hist_line;
675
#endif /* ESH_NFIRST */
676
location = hist_locate(shgd->hist_ptr,hline,hloff,count);
677
if (location.hist_command > histlines)
678
{
679
beep();
680
#ifdef ESH_NFIRST
681
location.hist_command = histlines;
682
location.hist_line = ep->in_mult;
683
#else
684
continue;
685
#endif /* ESH_NFIRST */
686
}
687
hline = location.hist_command;
688
hloff = location.hist_line;
689
common:
690
#ifdef ESH_NFIRST
691
location.hist_command = hline; /* save current position */
692
location.hist_line = hloff;
693
#endif
694
cur = 0;
695
draw(ep,UPDATE);
696
hist_copy((char*)out,MAXLINE, hline,hloff);
697
#if SHOPT_MULTIBYTE
698
ed_internal((char*)(out),out);
699
#endif /* SHOPT_MULTIBYTE */
700
drawline:
701
eol = genlen(out);
702
cur = eol;
703
draw(ep,UPDATE);
704
continue;
705
}
706
707
}
708
709
process:
710
711
if (c == (-1))
712
{
713
lookahead = 0;
714
beep();
715
*out = '\0';
716
}
717
draw(ep,FINAL);
718
tty_cooked(ERRIO);
719
if(ed->e_nlist)
720
{
721
ed->e_nlist = 0;
722
stakset(ed->e_stkptr,ed->e_stkoff);
723
}
724
if(c == '\n')
725
{
726
out[eol++] = '\n';
727
out[eol] = '\0';
728
ed_crlf(ep->ed);
729
}
730
#if SHOPT_MULTIBYTE
731
ed_external(out,buff);
732
#endif /* SHOPT_MULTIBYTE */
733
i = (int)strlen(buff);
734
if (i)
735
return(i);
736
return(-1);
737
}
738
739
static void show_info(Emacs_t *ep,const char *str)
740
{
741
register genchar *out = drawbuff;
742
register int c;
743
genchar string[LBUF];
744
int sav_cur = cur;
745
/* save current line */
746
genncpy(string,out,sizeof(string)/sizeof(*string));
747
*out = 0;
748
cur = 0;
749
#if SHOPT_MULTIBYTE
750
ed_internal(str,out);
751
#else
752
gencpy(out,str);
753
#endif /* SHOPT_MULTIBYTE */
754
draw(ep,UPDATE);
755
c = ed_getchar(ep->ed,0);
756
if(c!=' ')
757
ed_ungetchar(ep->ed,c);
758
/* restore line */
759
cur = sav_cur;
760
genncpy(out,string,sizeof(string)/sizeof(*string));
761
draw(ep,UPDATE);
762
}
763
764
static void putstring(Emacs_t* ep,register char *sp)
765
{
766
register int c;
767
while (c= *sp++)
768
putchar(ep->ed,c);
769
}
770
771
772
static int escape(register Emacs_t* ep,register genchar *out,int count)
773
{
774
register int i,value;
775
int digit,ch;
776
digit = 0;
777
value = 0;
778
while ((i=ed_getchar(ep->ed,0)),isdigit(i))
779
{
780
value *= 10;
781
value += (i - '0');
782
digit = 1;
783
}
784
if (digit)
785
{
786
ed_ungetchar(ep->ed,i) ;
787
#ifdef ESH_KAPPEND
788
++killing; /* don't modify killing signal */
789
#endif
790
return(value);
791
}
792
value = count;
793
if(value<0)
794
value = 1;
795
switch(ch=i)
796
{
797
case cntl('V'):
798
show_info(ep,fmtident(e_version));
799
return(-1);
800
case ' ':
801
ep->mark = cur;
802
return(-1);
803
804
#ifdef ESH_KAPPEND
805
case '+': /* M-+ = append next kill */
806
killing = 2;
807
return -1; /* no argument for next command */
808
#endif
809
810
case 'p': /* M-p == ^W^Y (copy stack == kill & yank) */
811
ed_ungetchar(ep->ed,cntl('Y'));
812
ed_ungetchar(ep->ed,cntl('W'));
813
#ifdef ESH_KAPPEND
814
killing = 0; /* start fresh */
815
#endif
816
return(-1);
817
818
case 'l': /* M-l == lower-case */
819
case 'd':
820
case 'c':
821
case 'f':
822
{
823
i = cur;
824
while(value-- && i<eol)
825
{
826
while ((out[i])&&(!isword(i)))
827
i++;
828
while ((out[i])&&(isword(i)))
829
i++;
830
}
831
if(ch=='l')
832
{
833
value = i-cur;
834
while (value-- > 0)
835
{
836
i = out[cur];
837
#if SHOPT_MULTIBYTE
838
if((i&~STRIP)==0 && isupper(i))
839
#else
840
if(isupper(i))
841
#endif /* SHOPT_MULTIBYTE */
842
{
843
i += 'a' - 'A';
844
out[cur] = i;
845
}
846
cur++;
847
}
848
draw(ep,UPDATE);
849
return(-1);
850
}
851
852
else if(ch=='f')
853
goto update;
854
else if(ch=='c')
855
{
856
ed_ungetchar(ep->ed,cntl('C'));
857
return(i-cur);
858
}
859
else
860
{
861
if (i-cur)
862
{
863
ed_ungetchar(ep->ed,cntl('D'));
864
#ifdef ESH_KAPPEND
865
++killing; /* keep killing signal */
866
#endif
867
return(i-cur);
868
}
869
beep();
870
return(-1);
871
}
872
}
873
874
875
case 'b':
876
case DELETE :
877
case '\b':
878
case 'h':
879
{
880
i = cur;
881
while(value-- && i>0)
882
{
883
i--;
884
while ((i>0)&&(!isword(i)))
885
i--;
886
while ((i>0)&&(isword(i-1)))
887
i--;
888
}
889
if(ch=='b')
890
goto update;
891
else
892
{
893
ed_ungetchar(ep->ed,usrerase);
894
#ifdef ESH_KAPPEND
895
++killing;
896
#endif
897
return(cur-i);
898
}
899
}
900
901
case '>':
902
ed_ungetchar(ep->ed,cntl('N'));
903
#ifdef ESH_NFIRST
904
if (ep->in_mult)
905
{
906
location.hist_command = histlines;
907
location.hist_line = ep->in_mult - 1;
908
}
909
else
910
{
911
location.hist_command = histlines - 1;
912
location.hist_line = 0;
913
}
914
#else
915
hline = histlines-1;
916
hloff = 0;
917
#endif /* ESH_NFIRST */
918
return(0);
919
920
case '<':
921
ed_ungetchar(ep->ed,cntl('P'));
922
hloff = 0;
923
#ifdef ESH_NFIRST
924
hline = hismin + 1;
925
return 0;
926
#else
927
return(hline-hismin);
928
#endif /* ESH_NFIRST */
929
930
931
case '#':
932
ed_ungetchar(ep->ed,'\n');
933
ed_ungetchar(ep->ed,(out[0]=='#')?cntl('D'):'#');
934
ed_ungetchar(ep->ed,cntl('A'));
935
return(-1);
936
case '_' :
937
case '.' :
938
{
939
genchar name[MAXLINE];
940
char buf[MAXLINE];
941
char *ptr;
942
ptr = hist_word(buf,MAXLINE,(count?count:-1));
943
if(ptr==0)
944
{
945
beep();
946
break;
947
}
948
if ((eol - cur) >= sizeof(name))
949
{
950
beep();
951
return(-1);
952
}
953
ep->mark = cur;
954
gencpy(name,&out[cur]);
955
while(*ptr)
956
{
957
out[cur++] = *ptr++;
958
eol++;
959
}
960
gencpy(&out[cur],name);
961
draw(ep,UPDATE);
962
return(-1);
963
}
964
#if KSHELL
965
966
#if SHOPT_EDPREDICT
967
case '\n': case '\t':
968
if(!ep->ed->hlist)
969
{
970
beep();
971
break;
972
}
973
if(ch=='\n')
974
ed_ungetchar(ep->ed,'\n');
975
#endif /* SHOPT_EDPREDICT */
976
/* file name expansion */
977
case cntl('[') : /* filename completion */
978
#if SHOPT_EDPREDICT
979
if(ep->ed->hlist)
980
{
981
value += ep->ed->hoff;
982
if(value > ep->ed->nhlist)
983
beep();
984
else
985
{
986
value = histlines - ep->ed->hlist[value-1]->index;
987
ed_histlist(ep->ed,0);
988
ed_ungetchar(ep->ed,cntl('P'));
989
return(value);
990
}
991
}
992
#endif /* SHOPT_EDPREDICT */
993
i = '\\';
994
case '*': /* filename expansion */
995
case '=': /* escape = - list all matching file names */
996
ep->mark = cur;
997
if(ed_expand(ep->ed,(char*)out,&cur,&eol,i,count) < 0)
998
{
999
if(ep->ed->e_tabcount==1)
1000
{
1001
ep->ed->e_tabcount=2;
1002
ed_ungetchar(ep->ed,cntl('\t'));
1003
return(-1);
1004
}
1005
beep();
1006
}
1007
else if(i=='=' || (i=='\\' && out[cur-1]=='/'))
1008
{
1009
draw(ep,REFRESH);
1010
if(count>0 || i=='\\')
1011
ep->ed->e_tabcount=0;
1012
else
1013
{
1014
i=ed_getchar(ep->ed,0);
1015
ed_ungetchar(ep->ed,i);
1016
if(isdigit(i))
1017
ed_ungetchar(ep->ed,ESC);
1018
}
1019
}
1020
else
1021
{
1022
if(i=='\\' && cur>ep->mark && (out[cur-1]=='/' || out[cur-1]==' '))
1023
ep->ed->e_tabcount=0;
1024
draw(ep,UPDATE);
1025
}
1026
return(-1);
1027
1028
/* search back for character */
1029
case cntl(']'): /* feature not in book */
1030
{
1031
int c = ed_getchar(ep->ed,1);
1032
if ((value == 0) || (value > eol))
1033
{
1034
beep();
1035
return(-1);
1036
}
1037
i = cur;
1038
if (i > 0)
1039
i--;
1040
while (i >= 0)
1041
{
1042
if (out[i] == c && --value==0)
1043
goto update;
1044
i--;
1045
}
1046
i = eol;
1047
while (i > cur)
1048
{
1049
if (out[i] == c && --value==0)
1050
break;
1051
i--;
1052
};
1053
1054
}
1055
update:
1056
cur = i;
1057
draw(ep,UPDATE);
1058
return(-1);
1059
1060
#ifdef _cmd_tput
1061
case cntl('L'): /* clear screen */
1062
sh_trap("tput clear", 0);
1063
draw(ep,REFRESH);
1064
return(-1);
1065
#endif
1066
case '[': /* feature not in book */
1067
switch(i=ed_getchar(ep->ed,1))
1068
{
1069
case 'A':
1070
#if SHOPT_EDPREDICT
1071
if(!ep->ed->hlist && cur>0 && eol==cur && (cur<(SEARCHSIZE-2) || ep->prevdirection == -2))
1072
#else
1073
if(cur>0 && eol==cur && (cur<(SEARCHSIZE-2) || ep->prevdirection == -2))
1074
#endif /* SHOPT_EDPREDICT */
1075
{
1076
if(ep->lastdraw==APPEND && ep->prevdirection != -2)
1077
{
1078
out[cur] = 0;
1079
gencpy((genchar*)lstring+1,out);
1080
#if SHOPT_MULTIBYTE
1081
ed_external((genchar*)lstring+1,lstring+1);
1082
#endif /* SHOPT_MULTIBYTE */
1083
*lstring = '^';
1084
ep->prevdirection = -2;
1085
}
1086
if(*lstring)
1087
{
1088
ed_ungetchar(ep->ed,'\r');
1089
ed_ungetchar(ep->ed,cntl('R'));
1090
return(-1);
1091
}
1092
}
1093
*lstring = 0;
1094
ed_ungetchar(ep->ed,cntl('P'));
1095
return(-1);
1096
case 'B':
1097
ed_ungetchar(ep->ed,cntl('N'));
1098
return(-1);
1099
case 'C':
1100
ed_ungetchar(ep->ed,cntl('F'));
1101
return(-1);
1102
case 'D':
1103
ed_ungetchar(ep->ed,cntl('B'));
1104
return(-1);
1105
case 'H':
1106
ed_ungetchar(ep->ed,cntl('A'));
1107
return(-1);
1108
case 'Y':
1109
ed_ungetchar(ep->ed,cntl('E'));
1110
return(-1);
1111
default:
1112
ed_ungetchar(ep->ed,i);
1113
}
1114
i = '_';
1115
1116
default:
1117
/* look for user defined macro definitions */
1118
if(ed_macro(ep->ed,i))
1119
# ifdef ESH_BETTER
1120
return(count); /* pass argument to macro */
1121
# else
1122
return(-1);
1123
# endif /* ESH_BETTER */
1124
#else
1125
update:
1126
cur = i;
1127
draw(ep,UPDATE);
1128
return(-1);
1129
1130
default:
1131
#endif /* KSHELL */
1132
beep();
1133
return(-1);
1134
}
1135
return(-1);
1136
}
1137
1138
1139
/*
1140
* This routine process all commands starting with ^X
1141
*/
1142
1143
static void xcommands(register Emacs_t *ep,int count)
1144
{
1145
register int i = ed_getchar(ep->ed,0);
1146
NOT_USED(count);
1147
switch(i)
1148
{
1149
case cntl('X'): /* exchange dot and mark */
1150
if (ep->mark > eol)
1151
ep->mark = eol;
1152
i = ep->mark;
1153
ep->mark = cur;
1154
cur = i;
1155
draw(ep,UPDATE);
1156
return;
1157
1158
#if KSHELL
1159
# ifdef ESH_BETTER
1160
case cntl('E'): /* invoke emacs on current command */
1161
if(ed_fulledit(ep->ed)==-1)
1162
beep();
1163
else
1164
{
1165
#if SHOPT_MULTIBYTE
1166
ed_internal((char*)drawbuff,drawbuff);
1167
#endif /* SHOPT_MULTIBYTE */
1168
ed_ungetchar(ep->ed,'\n');
1169
}
1170
return;
1171
1172
# define itos(i) fmtbase((long)(i),0,0)/* want signed conversion */
1173
1174
case cntl('H'): /* ^X^H show history info */
1175
{
1176
char hbuf[MAXLINE];
1177
1178
strcpy(hbuf, "Current command ");
1179
strcat(hbuf, itos(hline));
1180
if (hloff)
1181
{
1182
strcat(hbuf, " (line ");
1183
strcat(hbuf, itos(hloff+1));
1184
strcat(hbuf, ")");
1185
}
1186
if ((hline != location.hist_command) ||
1187
(hloff != location.hist_line))
1188
{
1189
strcat(hbuf, "; Previous command ");
1190
strcat(hbuf, itos(location.hist_command));
1191
if (location.hist_line)
1192
{
1193
strcat(hbuf, " (line ");
1194
strcat(hbuf, itos(location.hist_line+1));
1195
strcat(hbuf, ")");
1196
}
1197
}
1198
show_info(ep,hbuf);
1199
return;
1200
}
1201
# if 0 /* debugging, modify as required */
1202
case cntl('D'): /* ^X^D show debugging info */
1203
{
1204
char debugbuf[MAXLINE];
1205
1206
strcpy(debugbuf, "count=");
1207
strcat(debugbuf, itos(count));
1208
strcat(debugbuf, " eol=");
1209
strcat(debugbuf, itos(eol));
1210
strcat(debugbuf, " cur=");
1211
strcat(debugbuf, itos(cur));
1212
strcat(debugbuf, " crallowed=");
1213
strcat(debugbuf, itos(crallowed));
1214
strcat(debugbuf, " plen=");
1215
strcat(debugbuf, itos(plen));
1216
strcat(debugbuf, " w_size=");
1217
strcat(debugbuf, itos(w_size));
1218
1219
show_info(ep,debugbuf);
1220
return;
1221
}
1222
# endif /* debugging code */
1223
# endif /* ESH_BETTER */
1224
#endif /* KSHELL */
1225
1226
default:
1227
beep();
1228
return;
1229
}
1230
}
1231
1232
static void search(Emacs_t* ep,genchar *out,int direction)
1233
{
1234
#ifndef ESH_NFIRST
1235
Histloc_t location;
1236
#endif
1237
register int i,sl;
1238
genchar str_buff[LBUF];
1239
register genchar *string = drawbuff;
1240
/* save current line */
1241
int sav_cur = cur;
1242
genncpy(str_buff,string,sizeof(str_buff)/sizeof(*str_buff));
1243
string[0] = '^';
1244
string[1] = 'R';
1245
string[2] = '\0';
1246
sl = 2;
1247
cur = sl;
1248
draw(ep,UPDATE);
1249
while ((i = ed_getchar(ep->ed,1))&&(i != '\r')&&(i != '\n'))
1250
{
1251
if (i==usrerase || i==DELETE || i=='\b' || i==ERASECHAR)
1252
{
1253
if (sl > 2)
1254
{
1255
string[--sl] = '\0';
1256
cur = sl;
1257
draw(ep,UPDATE);
1258
}
1259
else
1260
goto restore;
1261
continue;
1262
}
1263
if(i == ep->ed->e_intr)
1264
goto restore;
1265
if (i==usrkill)
1266
{
1267
beep();
1268
goto restore;
1269
}
1270
if (i == '\\')
1271
{
1272
string[sl++] = '\\';
1273
string[sl] = '\0';
1274
cur = sl;
1275
draw(ep,APPEND);
1276
i = ed_getchar(ep->ed,1);
1277
string[--sl] = '\0';
1278
}
1279
string[sl++] = i;
1280
string[sl] = '\0';
1281
cur = sl;
1282
draw(ep,APPEND);
1283
}
1284
i = genlen(string);
1285
1286
if(ep->prevdirection == -2 && i!=2 || direction!=1)
1287
ep->prevdirection = -1;
1288
if (direction < 1)
1289
{
1290
ep->prevdirection = -ep->prevdirection;
1291
direction = 1;
1292
}
1293
else
1294
direction = -1;
1295
if (i != 2)
1296
{
1297
#if SHOPT_MULTIBYTE
1298
ed_external(string,(char*)string);
1299
#endif /* SHOPT_MULTIBYTE */
1300
strncpy(lstring,((char*)string)+2,SEARCHSIZE);
1301
lstring[SEARCHSIZE-1] = 0;
1302
ep->prevdirection = direction;
1303
}
1304
else
1305
direction = ep->prevdirection ;
1306
location = hist_find(shgd->hist_ptr,(char*)lstring,hline,1,direction);
1307
i = location.hist_command;
1308
if(i>0)
1309
{
1310
hline = i;
1311
#ifdef ESH_NFIRST
1312
hloff = location.hist_line = 0; /* display first line of multi line command */
1313
#else
1314
hloff = location.hist_line;
1315
#endif /* ESH_NFIRST */
1316
hist_copy((char*)out,MAXLINE, hline,hloff);
1317
#if SHOPT_MULTIBYTE
1318
ed_internal((char*)out,out);
1319
#endif /* SHOPT_MULTIBYTE */
1320
return;
1321
}
1322
if (i < 0)
1323
{
1324
beep();
1325
#ifdef ESH_NFIRST
1326
location.hist_command = hline;
1327
location.hist_line = hloff;
1328
#else
1329
hloff = 0;
1330
hline = histlines;
1331
#endif /* ESH_NFIRST */
1332
}
1333
restore:
1334
genncpy(string,str_buff,sizeof(str_buff)/sizeof(*str_buff));
1335
cur = sav_cur;
1336
return;
1337
}
1338
1339
1340
/* Adjust screen to agree with inputs: logical line and cursor */
1341
/* If 'first' assume screen is blank */
1342
/* Prompt is always kept on the screen */
1343
1344
static void draw(register Emacs_t *ep,Draw_t option)
1345
{
1346
#define NORMAL ' '
1347
#define LOWER '<'
1348
#define BOTH '*'
1349
#define UPPER '>'
1350
1351
register genchar *sptr; /* Pointer within screen */
1352
genchar nscreen[2*MAXLINE]; /* New entire screen */
1353
genchar *ncursor; /* New cursor */
1354
register genchar *nptr; /* Pointer to New screen */
1355
char longline; /* Line overflow */
1356
genchar *logcursor;
1357
genchar *nscend; /* end of logical screen */
1358
register int i;
1359
1360
nptr = nscreen;
1361
sptr = drawbuff;
1362
logcursor = sptr + cur;
1363
longline = NORMAL;
1364
ep->lastdraw = option;
1365
1366
if (option == FIRST || option == REFRESH)
1367
{
1368
ep->overflow = NORMAL;
1369
ep->cursor = ep->screen;
1370
ep->offset = 0;
1371
ep->cr_ok = crallowed;
1372
if (option == FIRST)
1373
{
1374
ep->scvalid = 1;
1375
return;
1376
}
1377
*ep->cursor = '\0';
1378
putstring(ep,Prompt); /* start with prompt */
1379
}
1380
1381
/*********************
1382
Do not update screen if pending characters
1383
**********************/
1384
1385
if ((lookahead)&&(option != FINAL))
1386
{
1387
1388
ep->scvalid = 0; /* Screen is out of date, APPEND will not work */
1389
1390
return;
1391
}
1392
1393
/***************************************
1394
If in append mode, cursor at end of line, screen up to date,
1395
the previous character was a 'normal' character,
1396
and the window has room for another character.
1397
Then output the character and adjust the screen only.
1398
*****************************************/
1399
1400
1401
i = *(logcursor-1); /* last character inserted */
1402
#if SHOPT_EDPREDICT
1403
if(option==FINAL)
1404
{
1405
if(ep->ed->hlist)
1406
ed_histlist(ep->ed,0);
1407
}
1408
else if((option==UPDATE||option==APPEND) && drawbuff[0]=='#' && cur>1 && cur==eol && drawbuff[cur-1]!='*')
1409
{
1410
int n;
1411
drawbuff[cur+1]=0;
1412
# if SHOPT_MULTIBYTE
1413
ed_external(drawbuff,(char*)drawbuff);
1414
# endif /*SHOPT_MULTIBYTE */
1415
n = ed_histgen(ep->ed,(char*)drawbuff);
1416
# if SHOPT_MULTIBYTE
1417
ed_internal((char*)drawbuff,drawbuff);
1418
# endif /*SHOPT_MULTIBYTE */
1419
if(ep->ed->hlist)
1420
{
1421
ed_histlist(ep->ed,n);
1422
putstring(ep,Prompt);
1423
ed_setcursor(ep->ed,ep->screen,0,ep->cursor-ep->screen, 0);
1424
}
1425
else
1426
ed_ringbell();
1427
1428
}
1429
#endif /* SHOPT_EDPREDICT */
1430
1431
if ((option == APPEND)&&(ep->scvalid)&&(*logcursor == '\0')&&
1432
print(i)&&((ep->cursor-ep->screen)<(w_size-1)))
1433
{
1434
putchar(ep->ed,i);
1435
*ep->cursor++ = i;
1436
*ep->cursor = '\0';
1437
return;
1438
}
1439
1440
/* copy the line */
1441
ncursor = nptr + ed_virt_to_phys(ep->ed,sptr,nptr,cur,0,0);
1442
nptr += genlen(nptr);
1443
sptr += genlen(sptr);
1444
nscend = nptr - 1;
1445
if(sptr == logcursor)
1446
ncursor = nptr;
1447
1448
/*********************
1449
Does ncursor appear on the screen?
1450
If not, adjust the screen offset so it does.
1451
**********************/
1452
1453
i = ncursor - nscreen;
1454
1455
if ((ep->offset && i<=ep->offset)||(i >= (ep->offset+w_size)))
1456
{
1457
/* Center the cursor on the screen */
1458
ep->offset = i - (w_size>>1);
1459
if (--ep->offset < 0)
1460
ep->offset = 0;
1461
}
1462
1463
/*********************
1464
Is the range of screen[0] thru screen[w_size] up-to-date
1465
with nscreen[offset] thru nscreen[offset+w_size] ?
1466
If not, update as need be.
1467
***********************/
1468
1469
nptr = &nscreen[ep->offset];
1470
sptr = ep->screen;
1471
1472
i = w_size;
1473
1474
while (i-- > 0)
1475
{
1476
1477
if (*nptr == '\0')
1478
{
1479
*(nptr + 1) = '\0';
1480
*nptr = ' ';
1481
}
1482
if (*sptr == '\0')
1483
{
1484
*(sptr + 1) = '\0';
1485
*sptr = ' ';
1486
}
1487
if (*nptr == *sptr)
1488
{
1489
nptr++;
1490
sptr++;
1491
continue;
1492
}
1493
setcursor(ep,sptr-ep->screen,*nptr);
1494
*sptr++ = *nptr++;
1495
#if SHOPT_MULTIBYTE
1496
while(*nptr==MARKER)
1497
{
1498
if(*sptr=='\0')
1499
*(sptr + 1) = '\0';
1500
*sptr++ = *nptr++;
1501
i--;
1502
ep->cursor++;
1503
}
1504
#endif /* SHOPT_MULTIBYTE */
1505
}
1506
if(ep->ed->e_multiline && option == REFRESH && ep->ed->e_nocrnl==0)
1507
ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1);
1508
1509
1510
/******************
1511
1512
Screen overflow checks
1513
1514
********************/
1515
1516
if (nscend >= &nscreen[ep->offset+w_size])
1517
{
1518
if (ep->offset > 0)
1519
longline = BOTH;
1520
else
1521
longline = UPPER;
1522
}
1523
else
1524
{
1525
if (ep->offset > 0)
1526
longline = LOWER;
1527
}
1528
1529
/* Update screen overflow indicator if need be */
1530
1531
if (longline != ep->overflow)
1532
{
1533
setcursor(ep,w_size,longline);
1534
ep->overflow = longline;
1535
}
1536
i = (ncursor-nscreen) - ep->offset;
1537
setcursor(ep,i,0);
1538
if(option==FINAL && ep->ed->e_multiline)
1539
setcursor(ep,nscend+1-nscreen,0);
1540
ep->scvalid = 1;
1541
return;
1542
}
1543
1544
/*
1545
* put the cursor to the <newp> position within screen buffer
1546
* if <c> is non-zero then output this character
1547
* cursor is set to reflect the change
1548
*/
1549
1550
static void setcursor(register Emacs_t *ep,register int newp,int c)
1551
{
1552
register int oldp = ep->cursor - ep->screen;
1553
newp = ed_setcursor(ep->ed, ep->screen, oldp, newp, 0);
1554
if(c)
1555
{
1556
putchar(ep->ed,c);
1557
newp++;
1558
}
1559
ep->cursor = ep->screen+newp;
1560
return;
1561
}
1562
1563
#if SHOPT_MULTIBYTE
1564
static int print(register int c)
1565
{
1566
return((c&~STRIP)==0 && isprint(c));
1567
}
1568
1569
static int _isword(register int c)
1570
{
1571
return((c&~STRIP) || isalnum(c) || c=='_');
1572
}
1573
#endif /* SHOPT_MULTIBYTE */
1574
1575