Edinburgh Speech Tools  2.4-release
editline.c
1 /****************************************************************************/
2 /* */
3 /* Copyright 1992 Simmule Turner and Rich Salz. All rights reserved. */
4 /* */
5 /* This software is not subject to any license of the American Telephone */
6 /* and Telegraph Company or of the Regents of the University of California. */
7 /* */
8 /* Permission is granted to anyone to use this software for any purpose on */
9 /* any computer system, and to alter it and redistribute it freely, subject */
10 /* to the following restrictions: */
11 /* 1. The authors are not responsible for the consequences of use of this */
12 /* software, no matter how awful, even if they arise from flaws in it. */
13 /* 2. The origin of this software must not be misrepresented, either by */
14 /* explicit claim or by omission. Since few users ever read sources, */
15 /* credits must appear in the documentation. */
16 /* 3. Altered versions must be plainly marked as such, and must not be */
17 /* misrepresented as being the original software. Since few users */
18 /* ever read sources, credits must appear in the documentation. */
19 /* 4. This notice may not be removed or altered. */
20 /* */
21 /****************************************************************************/
22 /* */
23 /* This is a line-editing library, it can be linked into almost any */
24 /* program to provide command-line editing and recall. */
25 /* */
26 /* Posted to comp.sources.misc Sun, 2 Aug 1992 03:05:27 GMT */
27 /* by rsalz@osf.org (Rich $alz) */
28 /* */
29 /****************************************************************************/
30 /* */
31 /* The version contained here has some modifications by awb@cstr.ed.ac.uk */
32 /* (Alan W Black) in order to integrate it with the Edinburgh Speech Tools */
33 /* library and Scheme-in-one-defun in particular, though these changes */
34 /* have a much more general use that just us. All modifications to */
35 /* to this work are continued with the same copyright above. That is */
36 /* this version of editline does not have the the "no commercial use" */
37 /* restriction that some of the rest of the EST library may have */
38 /* awb Dec 30 1998 */
39 /* */
40 /* Specific additions (there are other smaller ones too, all marked): */
41 /* some ansificiation and prototypes added */
42 /* storage and retrieval of history over sessions */
43 /* user definable history completion */
44 /* possibles listing in completion */
45 /* reverse incremental search */
46 /* lines longer than window width (mostly) */
47 /* reasonable support for 8 bit chars in languages other than English */
48 /* */
49 /****************************************************************************/
50 
51 /* $Revision: 1.6 $
52 **
53 ** Main editing routines for editline library.
54 */
55 #include "editline.h"
56 #include "EST_unix.h"
57 #include <ctype.h>
58 
59 /*
60 ** Manifest constants.
61 */
62 #define SCREEN_WIDTH 80
63 #define SCREEN_ROWS 24
64 #define NO_ARG (-1)
65 #define DEL 127
66 #define ESC 0x1b
67 #define CTL(x) (char)((x) & 0x1F)
68 #define ISCTL(x) ((x) && (x) < ' ')
69 #define UNCTL(x) (char)((x) + 64)
70 #define META(x) (char)((x) | 0x80)
71 #define ISMETA(x) ((x) & 0x80)
72 #define UNMETA(x) (char)((x) & 0x7F)
73 /* modified by awb to allow specifcation of history size at run time */
74 /* (though only once) */
75 int editline_histsize=256;
76 /* If this is defined it'll be called for completion first, before the */
77 /* internal file name completion will be */
78 EL_USER_COMPLETION_FUNCTION_TYPE*el_user_completion_function = NULL;
79 
80 /*
81 ** The type of case-changing to perform.
82 */
83 typedef enum _CASE {
84  TOupper, TOlower, TOcapitalize
85 } CASE;
86 
87 /*
88 ** Key to command mapping.
89 */
90 typedef struct _KEYMAP {
91  ECHAR Key;
92  STATUS (*Function)();
93 } KEYMAP;
94 
95 /*
96 ** Command history structure.
97 */
98 typedef struct _HISTORY {
99  int Size;
100  int Pos;
101  ECHAR **Lines;
102 } HISTORY;
103 
104 /*
105 ** Globals.
106 */
107 int rl_eof;
108 int rl_erase;
109 int rl_intr;
110 int rl_kill;
111 
112 ECHAR el_NIL[] = "";
113 extern CONST ECHAR *el_Input;
114 STATIC ECHAR *Line = NULL;
115 STATIC CONST char *Prompt = NULL;
116 STATIC ECHAR *Yanked = NULL;
117 STATIC char *Screen = NULL;
118 /* STATIC char NEWLINE[]= CRLF; */
119 STATIC HISTORY H;
120 int rl_quit;
121 STATIC int Repeat;
122 STATIC int End;
123 STATIC int Mark;
124 STATIC int OldPoint;
125 STATIC int Point;
126 extern int el_PushBack;
127 extern int el_Pushed;
128 FORWARD KEYMAP Map[33];
129 FORWARD KEYMAP MetaMap[64];
130 STATIC ESIZE_T Length;
131 STATIC ESIZE_T ScreenCount;
132 STATIC ESIZE_T ScreenSize;
133 STATIC ECHAR *backspace = NULL;
134 STATIC ECHAR *upline = NULL;
135 STATIC ECHAR *clrpage = NULL;
136 STATIC ECHAR *downline = NULL;
137 STATIC ECHAR *move_right = NULL;
138 STATIC ECHAR *newline = NULL;
139 STATIC ECHAR *bol = NULL;
140 STATIC ECHAR *nextline = NULL;
141 STATIC int TTYwidth;
142 STATIC int TTYrows;
143 STATIC int RequireNLforWrap = 1;
144 STATIC int el_intr_pending = 0;
145 int el_no_echo = 0; /* e.g under emacs */
146 
147 /* A little ansification with prototypes -- awb */
148 extern void TTYflush();
149 STATIC void TTYput(ECHAR c);
150 STATIC void TTYputs(ECHAR *p);
151 STATIC void TTYshow(ECHAR c);
152 STATIC void TTYstring(ECHAR *p);
153 extern unsigned int TTYget();
154 STATIC void TTYinfo();
155 STATIC void print_columns(int ac, char **av);
156 STATIC void reposition(int reset);
157 STATIC void left(STATUS Change);
158 STATIC void right(STATUS Change);
159 STATIC STATUS ring_bell();
160 #if 0
161 STATIC STATUS do_macro(unsigned int c);
162 #endif
163 STATIC STATUS do_forward(STATUS move);
164 STATIC STATUS do_case(ECHAR type);
165 STATIC STATUS case_down_word();
166 STATIC STATUS case_up_word();
167 STATIC void ceol();
168 STATIC void clear_line();
169 STATIC STATUS insert_string(ECHAR *p);
170 STATIC ECHAR *next_hist();
171 STATIC ECHAR *prev_hist();
172 STATIC STATUS do_insert_hist(ECHAR *p);
173 STATIC STATUS do_hist(ECHAR *(*move)());
174 STATIC STATUS h_next();
175 STATIC STATUS h_prev();
176 STATIC STATUS h_first();
177 STATIC STATUS h_last();
178 STATIC int substrcmp(char *text, char *pat, int len);
179 STATIC ECHAR *search_hist(ECHAR *search, ECHAR *(*move)());
180 STATIC STATUS h_search();
181 STATIC STATUS fd_char();
182 STATIC void save_yank(int begin, int i);
183 STATIC STATUS delete_string(int count);
184 STATIC STATUS bk_char();
185 STATIC STATUS bk_del_char();
186 STATIC STATUS redisplay();
187 STATIC STATUS kill_line();
188 STATIC char *rsearch_hist(char *patt, int *lpos,int *cpos);
189 STATIC STATUS h_risearch();
190 STATIC STATUS insert_char(int c);
191 STATIC STATUS meta();
192 STATIC STATUS emacs(unsigned int c);
193 STATIC STATUS TTYspecial(unsigned int c);
194 STATIC ECHAR *editinput();
195 STATIC void hist_add(ECHAR *p);
196 STATIC STATUS beg_line();
197 STATIC STATUS del_char();
198 STATIC STATUS end_line();
199 STATIC ECHAR *find_word();
200 STATIC STATUS c_complete();
201 STATIC STATUS c_possible();
202 STATIC STATUS accept_line();
203 STATIC STATUS transpose();
204 STATIC STATUS quote();
205 STATIC STATUS wipe();
206 STATIC STATUS mk_set();
207 STATIC STATUS exchange();
208 STATIC STATUS yank();
209 STATIC STATUS copy_region();
210 STATIC STATUS move_to_char();
211 STATIC STATUS fd_word();
212 STATIC STATUS fd_kill_word();
213 STATIC STATUS bk_word();
214 STATIC STATUS bk_kill_word();
215 STATIC int argify(ECHAR *line, ECHAR ***avp);
216 STATIC STATUS last_argument();
217 
218 /* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */
219 int rl_meta_chars = 0;
220 
221 /*
222 ** Declarations.
223 */
224 STATIC ECHAR *editinput();
225 #if defined(USE_TERMCAP)
226 extern char *getenv();
227 extern char *tgetstr();
228 extern int tgetent();
229 extern int tgetnum();
230 #endif /* defined(USE_TERMCAP) */
231 
232 /*
233 ** TTY input/output functions.
234 */
235 
236 void TTYflush()
237 {
238  if (ScreenCount) {
239  if (el_no_echo == 0)
240  (void)write(1, Screen, ScreenCount);
241  ScreenCount = 0;
242  }
243 }
244 
245 STATIC void TTYput(ECHAR c)
246 {
247  Screen[ScreenCount] = c;
248  if (++ScreenCount >= ScreenSize - 1) {
249  ScreenSize += SCREEN_INC;
250  RENEW(Screen, char, ScreenSize);
251  }
252 }
253 
254 STATIC void TTYputs(ECHAR *p)
255 {
256  while (*p)
257  TTYput(*p++);
258 }
259 
260 STATIC void TTYshow(ECHAR c)
261 {
262  if (c == DEL) {
263  TTYput('^');
264  TTYput('?');
265  }
266  else if (ISCTL(c)) {
267  TTYput('^');
268  TTYput(UNCTL(c));
269  }
270  else if (rl_meta_chars && ISMETA(c)) {
271  TTYput('M');
272  TTYput('-');
273  TTYput(UNMETA(c));
274  }
275  else
276  TTYput(c);
277 }
278 
279 STATIC void TTYstring(ECHAR *p)
280 {
281  while (*p)
282  TTYshow(*p++);
283 }
284 
285 #if 0
286 /* Old one line version */
287 #define TTYback() (backspace ? TTYputs((ECHAR *)backspace) : TTYput('\b'))
288 #endif
289 
290 STATIC int printlen(CONST char *p)
291 {
292  int len = 0;
293 
294  for (len=0; *p; p++)
295  if ((*p == DEL) || (ISCTL(*p)))
296  len += 2;
297  else if (rl_meta_chars && ISMETA(*p))
298  len += 3;
299  else
300  len += 1;
301 
302  return len;
303 }
304 
305 STATIC int screen_pos()
306 {
307  /* Returns the number of characters printed from beginning of line */
308  /* includes the size of the prompt and and meta/ctl char expansions */
309  int p = strlen(Prompt);
310  int i;
311 
312  for (i=0; i < Point; i++)
313  if ((Line[i] == DEL) ||
314  (ISCTL(Line[i])))
315  p += 2;
316  else if (rl_meta_chars && ISMETA(Line[i]))
317  p += 3;
318  else
319  p += 1;
320 
321  return p;
322 }
323 
324 STATIC void TTYback()
325 {
326  /* awb: added upline (if supported) when back goes over line boundary */
327  int i;
328  int sp = screen_pos();
329 
330  if (upline && sp && (sp%TTYwidth == 0))
331  { /* move up a line and move to the end */
332  TTYputs(upline);
333  TTYputs(bol);
334  for (i=0; i < TTYwidth; i++)
335  TTYputs(move_right);
336  }
337  else if (backspace)
338  TTYputs((ECHAR *)backspace);
339  else
340  TTYput('\b');
341 }
342 
343 STATIC void TTYinfo()
344 {
345  static int init;
346 #if defined(USE_TERMCAP)
347  char *term;
348  char *buff;
349  char *buff2;
350  char *bp;
351 #endif /* defined(USE_TERMCAP) */
352 #if defined(TIOCGWINSZ)
353  struct winsize W;
354 #endif /* defined(TIOCGWINSZ) */
355 
356  if (init) {
357 #if defined(TIOCGWINSZ)
358  /* Perhaps we got resized. */
359  if (ioctl(0, TIOCGWINSZ, &W) >= 0
360  && W.ws_col > 0 && W.ws_row > 0) {
361  TTYwidth = (int)W.ws_col;
362  TTYrows = (int)W.ws_row;
363  }
364 #endif /* defined(TIOCGWINSZ) */
365  return;
366  }
367  init++;
368 
369  TTYwidth = TTYrows = 0;
370 #if defined(USE_TERMCAP)
371  buff = walloc(char,2048);
372  buff2 = walloc(char,2048);
373  bp = &buff2[0];
374  if ((term = getenv("TERM")) == NULL)
375  term = "dumb";
376  if (tgetent(buff, term) < 0) {
377  TTYwidth = SCREEN_WIDTH;
378  TTYrows = SCREEN_ROWS;
379  return;
380  }
381  backspace = (ECHAR *)tgetstr("le", &bp);
382  upline = (ECHAR *)tgetstr("up", &bp);
383  clrpage = (ECHAR *)tgetstr("cl", &bp);
384  nextline = (ECHAR *)tgetstr("nl", &bp);
385  if (nextline==NULL)
386  nextline = (ECHAR *)"\n";
387  if (strncmp(term, "pcansi", 6)==0 || strncmp(term, "cygwin", 6)==0)
388  {
389  bol = (ECHAR *)"\033[0G";
390  RequireNLforWrap = 0; /* doesn't require nl to get to next line */
391  }
392  else
393  bol = (ECHAR *)tgetstr("cr", &bp);
394  if (bol==NULL)
395  bol = (ECHAR *)"\r";
396 
397  newline= walloc(ECHAR, 20);
398  strcpy((char *)newline,(char *)bol);
399  strcat((char *)newline,(char *)nextline);
400 
401  downline = (ECHAR *)newline;
402  move_right = (ECHAR *)tgetstr("nd", &bp);
403  if (!move_right || !downline)
404  upline = NULL; /* terminal doesn't support enough so fall back */
405  TTYwidth = tgetnum("co");
406  TTYrows = tgetnum("li");
407 #endif /* defined(USE_TERMCAP) */
408 
409 #if defined(TIOCGWINSZ)
410  if (ioctl(0, TIOCGWINSZ, &W) >= 0) {
411  TTYwidth = (int)W.ws_col;
412  TTYrows = (int)W.ws_row;
413  }
414 #endif /* defined(TIOCGWINSZ) */
415 
416  if (TTYwidth <= 0 || TTYrows <= 0) {
417  TTYwidth = SCREEN_WIDTH;
418  TTYrows = SCREEN_ROWS;
419  }
420 }
421 
422 
423 /*
424 ** Print an array of words in columns.
425 */
426 STATIC void print_columns(int ac, char **av)
427 {
428  ECHAR *p;
429  int i,c;
430  int j;
431  int k;
432  int len;
433  int skip;
434  int longest;
435  int cols;
436  char info1[1024];
437 
438  if (ac > 99)
439  {
440  TTYputs((ECHAR *)newline);
441  sprintf(info1,"There are %d possibilities. Do you really \n",ac);
442  TTYputs((ECHAR *)info1);
443  TTYputs((ECHAR *)"want to see them all (y/n) ? ");
444  while (((c = TTYget()) != EOF) && ((strchr("YyNn ",c) == NULL)))
445  ring_bell();
446  if (strchr("Nn",c) != NULL)
447  {
448  TTYputs((ECHAR *)newline);
449  return;
450  }
451  }
452 
453  /* Find longest name, determine column count from that. */
454  for (longest = 0, i = 0; i < ac; i++)
455  if ((j = strlen((char *)av[i])) > longest)
456  longest = j;
457  cols = TTYwidth / (longest + 3);
458  if (cols < 1) cols = 1;
459 
460  TTYputs((ECHAR *)newline);
461  for (skip = ac / cols + 1, i = 0; i < skip; i++) {
462  for (j = i; j < ac; j += skip) {
463  for (p = (ECHAR *)av[j], len = strlen((char *)p), k = len;
464  --k >= 0; p++)
465  TTYput(*p);
466  if (j + skip < ac)
467  while (++len < longest + 3)
468  TTYput(' ');
469  }
470  TTYputs((ECHAR *)newline);
471  }
472 }
473 
474 STATIC void reposition(int reset)
475 {
476  int i,PPoint;
477  int pos;
478  char ppp[2];
479 
480  if (reset)
481  {
482  TTYputs(bol);
483  for (i=screen_pos()/TTYwidth; i > 0; i--)
484  if (upline) TTYputs(upline);
485  }
486  TTYputs((ECHAR *)Prompt);
487  pos = printlen(Prompt);
488  ppp[1] = '\0';
489  for (i = 0; i < End; i++)
490  {
491  ppp[0] = Line[i];
492  TTYshow(Line[i]);
493  pos += printlen(ppp);
494  if ((pos%TTYwidth) == 0)
495  if (RequireNLforWrap && downline) TTYputs(downline);
496  }
497  PPoint = Point;
498  for (Point = End;
499  Point > PPoint;
500  Point--)
501  {
502  if (rl_meta_chars && ISMETA(Line[Point]))
503  {
504  TTYback();
505  TTYback();
506  }
507  else if (ISCTL(Line[Point]))
508  TTYback();
509  TTYback();
510  }
511  Point = PPoint;
512 }
513 
514 STATIC void left(STATUS Change)
515 {
516  TTYback();
517  if (Point) {
518  if (ISCTL(Line[Point - 1]))
519  TTYback();
520  else if (rl_meta_chars && ISMETA(Line[Point - 1])) {
521  TTYback();
522  TTYback();
523  }
524  }
525  if (Change == CSmove)
526  Point--;
527 }
528 
529 STATIC void right(STATUS Change)
530 {
531  TTYshow(Line[Point]);
532  if (Change == CSmove)
533  Point++;
534  if ((screen_pos())%TTYwidth == 0)
535  if (downline && RequireNLforWrap) TTYputs(downline);
536 }
537 
538 STATIC STATUS ring_bell()
539 {
540  TTYput('\07');
541  TTYflush();
542  return CSstay;
543 }
544 
545 #if 0
546 STATIC STATUS do_macro(unsigned int c)
547 {
548  ECHAR name[4];
549 
550  name[0] = '_';
551  name[1] = c;
552  name[2] = '_';
553  name[3] = '\0';
554 
555  if ((el_Input = (ECHAR *)getenv((char *)name)) == NULL) {
556  el_Input = el_NIL;
557  return ring_bell();
558  }
559  return CSstay;
560 }
561 #endif
562 
563 STATIC STATUS do_forward(STATUS move)
564 {
565  int i;
566  ECHAR *p;
567  (void) move;
568 
569  i = 0;
570  do {
571  p = &Line[Point];
572  for ( ; Point < End && (*p == ' ' || !isalnum(*p)); p++)
573  right(CSmove);
574 
575  for (; Point < End && isalnum(*p); p++)
576  right(CSmove);
577 
578  if (Point == End)
579  break;
580  } while (++i < Repeat);
581 
582  return CSstay;
583 }
584 
585 STATIC STATUS do_case(ECHAR type)
586 {
587  int i;
588  int end;
589  int count;
590  ECHAR *p;
591  int OP;
592 
593  OP = Point;
594  (void)do_forward(CSstay);
595  if (OP != Point) {
596  if ((count = Point - OP) < 0)
597  count = -count;
598  for ( ; Point > OP; Point --)
599  TTYback();
600  if ((end = Point + count) > End)
601  end = End;
602  for (i = Point, p = &Line[Point]; Point < end; p++) {
603  if ((type == TOupper) ||
604  ((type == TOcapitalize) && (Point == i)))
605  {
606  if (islower(*p))
607  *p = toupper(*p);
608  }
609  else if (isupper(*p))
610  *p = tolower(*p);
611  right(CSmove);
612  }
613  }
614  return CSstay;
615 }
616 
617 STATIC STATUS case_down_word()
618 {
619  return do_case(TOlower);
620 }
621 
622 STATIC STATUS case_up_word()
623 {
624  return do_case(TOupper);
625 }
626 
627 STATIC STATUS case_cap_word()
628 {
629  return do_case(TOcapitalize);
630 }
631 
632 STATIC void ceol()
633 {
634  int extras;
635  int i, PPoint;
636  ECHAR *p;
637 
638  PPoint = Point;
639  for (extras = 0, i = Point, p = &Line[i]; i < End; i++, p++) {
640  Point++;
641  TTYput(' ');
642  if (ISCTL(*p)) {
643  TTYput(' ');
644  extras++;
645  }
646  else if (rl_meta_chars && ISMETA(*p)) {
647  TTYput(' ');
648  TTYput(' ');
649  extras += 2;
650  }
651  else if ((screen_pos())%TTYwidth == 0)
652  if (downline && RequireNLforWrap) TTYputs(downline);
653  }
654 
655  Point = End;
656  for (Point = End;
657  Point > PPoint;
658  Point--)
659  {
660  if (rl_meta_chars && ISMETA(Line[Point-1]))
661  {
662  TTYback();
663  TTYback();
664  }
665  else if (ISCTL(Line[Point-1]))
666  TTYback();
667  TTYback();
668  }
669  Point = PPoint;
670 
671 }
672 
673 STATIC void clear_line()
674 {
675  int i;
676  TTYputs(bol);
677  for (i=screen_pos()/TTYwidth; i > 0; i--)
678  if (upline) TTYputs(upline);
679  for (i=0; i < strlen(Prompt); i++)
680  TTYput(' ');
681  Point = 0;
682  ceol();
683  TTYputs(bol);
684  /* In case the prompt is more than one line long */
685  for (i=screen_pos()/TTYwidth; i > 0; i--)
686  if (upline) TTYputs(upline);
687  Point = 0;
688  End = 0;
689  Line[0] = '\0';
690 }
691 
692 STATIC STATUS insert_string(ECHAR *p)
693 {
694  ESIZE_T len;
695  int i,pos0,pos1;
696  ECHAR *new;
697  ECHAR *q;
698 
699  len = strlen((char *)p);
700  if (End + len >= Length) {
701  if ((new = NEW(ECHAR, Length + len + MEM_INC)) == NULL)
702  return CSstay;
703  if (Length) {
704  COPYFROMTO(new, Line, Length);
705  DISPOSE(Line);
706  }
707  Line = new;
708  Length += len + MEM_INC;
709  }
710 
711  for (q = &Line[Point], i = End - Point; --i >= 0; )
712  q[len + i] = q[i];
713  COPYFROMTO(&Line[Point], p, len);
714  End += len;
715  Line[End] = '\0';
716  pos0 = screen_pos();
717  pos1 = printlen((char *)&Line[Point]);
718  TTYstring(&Line[Point]);
719  Point += len;
720  if ((pos0+pos1)%TTYwidth == 0)
721  if (downline && RequireNLforWrap) TTYputs(downline);
722  /* if the line is longer than TTYwidth this may put the cursor */
723  /* on the next line and confuse some other parts, so put it back */
724  /* at Point */
725  if (upline && (Point != End))
726  {
727  pos0 = screen_pos();
728  pos1 = printlen((char *)&Line[Point]);
729  for (i=((pos0%TTYwidth)+pos1)/TTYwidth; i > 0; i--)
730  if (upline) TTYputs(upline);
731  TTYputs(bol);
732  for (i=0 ; i < (pos0%TTYwidth); i++)
733  TTYputs(move_right);
734  }
735 
736  return Point == End ? CSstay : CSmove;
737 }
738 
739 
740 STATIC ECHAR *next_hist()
741 {
742  return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos];
743 }
744 
745 STATIC ECHAR *prev_hist()
746 {
747  return H.Pos == 0 ? NULL : H.Lines[--H.Pos];
748 }
749 
750 STATIC STATUS do_insert_hist(ECHAR *p)
751 {
752  int i;
753  if (p == NULL)
754  return ring_bell();
755  for (i=screen_pos()/TTYwidth; i > 0; i--)
756  if (upline) TTYputs(upline);
757  Point = 0;
758  reposition(1);
759  ceol();
760  End = 0;
761  return insert_string(p);
762 }
763 
764 STATIC STATUS do_hist(ECHAR *(*move)())
765 {
766  ECHAR *p;
767  int i;
768 
769  i = 0;
770  do {
771  if ((p = (*move)()) == NULL)
772  return ring_bell();
773  } while (++i < Repeat);
774  return do_insert_hist(p);
775 }
776 
777 STATIC STATUS h_next()
778 {
779  return do_hist(next_hist);
780 }
781 
782 STATIC STATUS h_prev()
783 {
784  return do_hist(prev_hist);
785 }
786 
787 STATIC STATUS h_first()
788 {
789  return do_insert_hist(H.Lines[H.Pos = 0]);
790 }
791 
792 STATIC STATUS h_last()
793 {
794  return do_insert_hist(H.Lines[H.Pos = H.Size - 1]);
795 }
796 
797 /*
798 ** Return zero if pat appears as a substring in text.
799 */
800 STATIC int substrcmp(char *text, char *pat, int len)
801 {
802  ECHAR c;
803 
804  if ((c = *pat) == '\0')
805  return *text == '\0';
806  for ( ; *text; text++)
807  if (*text == c && strncmp(text, pat, len) == 0)
808  return 0;
809  return 1;
810 }
811 
812 STATIC ECHAR *search_hist(ECHAR *search, ECHAR *(*move)())
813 {
814  static ECHAR *old_search;
815  int len;
816  int pos;
817  int (*match)();
818  char *pat;
819 
820  /* Save or get remembered search pattern. */
821  if (search && *search) {
822  if (old_search)
823  DISPOSE(old_search);
824  old_search = (ECHAR *)STRDUP((const char *)search);
825  }
826  else {
827  if (old_search == NULL || *old_search == '\0')
828  return NULL;
829  search = old_search;
830  }
831 
832  /* Set up pattern-finder. */
833  if (*search == '^') {
834  match = strncmp;
835  pat = (char *)(search + 1);
836  }
837  else {
838  match = substrcmp;
839  pat = (char *)search;
840  }
841  len = strlen(pat);
842 
843  for (pos = H.Pos; (*move)() != NULL; )
844  if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0)
845  return H.Lines[H.Pos];
846  H.Pos = pos;
847  return NULL;
848 }
849 
850 STATIC STATUS h_search()
851 {
852  static int Searching;
853  CONST char *old_prompt;
854  ECHAR *(*move)();
855  ECHAR *p;
856 
857  if (Searching)
858  return ring_bell();
859  Searching = 1;
860 
861  clear_line();
862  old_prompt = Prompt;
863  Prompt = "Search: ";
864  TTYputs((ECHAR *)Prompt);
865  move = Repeat == NO_ARG ? prev_hist : next_hist;
866  p = search_hist(editinput(), move);
867  clear_line();
868  Prompt = old_prompt;
869  TTYputs((ECHAR *)Prompt);
870 
871  Searching = 0;
872  return do_insert_hist(p);
873 }
874 
875 STATIC STATUS fd_char()
876 {
877  int i;
878 
879  i = 0;
880  do {
881  if (Point >= End)
882  break;
883  right(CSmove);
884  } while (++i < Repeat);
885  return CSstay;
886 }
887 
888 STATIC void save_yank(int begin, int i)
889 {
890  if (Yanked) {
891  DISPOSE(Yanked);
892  Yanked = NULL;
893  }
894 
895  if (i < 1)
896  return;
897 
898  if ((Yanked = NEW(ECHAR, (ESIZE_T)i + 1)) != NULL) {
899  COPYFROMTO(Yanked, &Line[begin], i);
900  Yanked[i] = '\0';
901  }
902 }
903 
904 STATIC STATUS delete_string(int count)
905 {
906  int i;
907  int pos0,pos1,q;
908  char *tLine;
909 
910  if (count <= 0 || End == Point)
911  return ring_bell();
912 
913  if (Point + count > End && (count = End - Point) <= 0)
914  return CSstay;
915 
916  if (count > 1)
917  save_yank(Point, count);
918 
919  tLine = STRDUP((char *)Line);
920  ceol();
921  for (q = Point, i = End - (Point + count) + 1; --i >= 0; q++)
922  Line[q] = tLine[q+count];
923  wfree(tLine);
924  End -= count;
925  pos0 = screen_pos();
926  pos1 = printlen((char *)&Line[Point]);
927  TTYstring(&Line[Point]);
928  if ((pos1 > 0) && (pos0+pos1)%TTYwidth == 0)
929  if (downline && RequireNLforWrap) TTYputs(downline);
930  /* if the line is longer than TTYwidth this may put the cursor */
931  /* on the next line and confuse some other parts, so put it back */
932  /* at Point */
933  if (upline)
934  {
935  for (i=((pos0%TTYwidth)+pos1)/TTYwidth; i > 0; i--)
936  if (upline) TTYputs(upline);
937  TTYputs(bol);
938  for (i=0 ; i < (pos0%TTYwidth); i++)
939  TTYputs(move_right);
940  }
941 
942  return CSmove;
943 }
944 
945 STATIC STATUS bk_char()
946 {
947  int i;
948 
949  i = 0;
950  do {
951  if (Point == 0)
952  break;
953  left(CSmove);
954  } while (++i < Repeat);
955 
956  return CSstay;
957 }
958 
959 STATIC STATUS bk_del_char()
960 {
961  int i;
962 
963  i = 0;
964  do {
965  if (Point == 0)
966  break;
967  left(CSmove);
968  } while (++i < Repeat);
969 
970  return delete_string(i);
971 }
972 
973 STATIC STATUS redisplay()
974 {
975  if (clrpage) TTYputs(clrpage);
976  else
977  TTYputs((ECHAR *)newline);
978 /* TTYputs((ECHAR *)Prompt);
979  TTYstring(Line); */
980  return CSmove;
981 }
982 
983 STATIC STATUS kill_line()
984 {
985  int i;
986 
987  if (Repeat != NO_ARG) {
988  if (Repeat < Point) {
989  i = Point;
990  Point = Repeat;
991  reposition(1);
992  (void)delete_string(i - Point);
993  }
994  else if (Repeat > Point) {
995  right(CSmove);
996  (void)delete_string(Repeat - Point - 1);
997  }
998  return CSmove;
999  }
1000 
1001  save_yank(Point, End - Point);
1002  ceol();
1003  Line[Point] = '\0';
1004  End = Point;
1005  return CSstay;
1006 }
1007 
1008 STATIC char *rsearch_hist(char *patt, int *lpos,int *cpos)
1009 {
1010  /* Extension by awb to do reverse incremental searches */
1011 
1012  for (; *lpos > 0; (*lpos)--)
1013  {
1014  for ( ; (*cpos) >= 0 ; (*cpos)--)
1015  {
1016 /* fprintf(stderr,"comparing %d %s %s\n",*lpos,patt,H.Lines[*lpos]+*cpos); */
1017  if (strncmp(patt,(char *)H.Lines[*lpos]+*cpos,strlen(patt)) == 0)
1018  { /* found a match */
1019  return (char *)H.Lines[*lpos];
1020  }
1021  }
1022  if ((*lpos) > 0)
1023  *cpos = strlen((char *)H.Lines[(*lpos)-1]);
1024  }
1025  return NULL; /* no match found */
1026 }
1027 
1028 STATIC STATUS h_risearch()
1029 {
1030  STATUS s;
1031  CONST char *old_prompt;
1032  char *pat, *hist, *nhist;
1033  char *nprompt;
1034  int patend, i;
1035  ECHAR c;
1036  int lpos,cpos;
1037 
1038  old_prompt = Prompt;
1039 
1040  nprompt = walloc(char,80+160);
1041  pat = walloc(char,80);
1042  patend=0;
1043  pat[0] = '\0';
1044  hist = "";
1045  lpos = H.Pos; /* where the search has to start from */
1046  cpos = strlen((char *)H.Lines[lpos]);
1047  do
1048  {
1049  sprintf(nprompt,"(reverse-i-search)`%s': ",pat);
1050  Prompt = nprompt;
1051  kill_line();
1052  do_insert_hist((ECHAR *)hist);
1053  if (patend != 0)
1054  for (i=strlen((char *)H.Lines[lpos]); i>cpos; i--) bk_char();
1055  c = TTYget();
1056  if ((c >= ' ') || (c == CTL('R')))
1057  {
1058  if (c == CTL('R'))
1059  cpos--;
1060  else if (patend < 79)
1061  {
1062  pat[patend]=c;
1063  patend++;
1064  pat[patend]='\0';
1065  }
1066  else /* too long */
1067  {
1068  ring_bell();
1069  continue;
1070  }
1071  nhist = rsearch_hist(pat,&lpos,&cpos);
1072  if (nhist != NULL)
1073  {
1074  hist = nhist;
1075  H.Pos = lpos;
1076  }
1077  else
1078  { /* oops, no match */
1079  ring_bell();
1080  if (c != CTL('R'))
1081  {
1082  patend--;
1083  pat[patend] = '\0';
1084  }
1085  }
1086  }
1087  } while ((c >= ' ') || (c == CTL('R')));
1088 
1089  /* Tidy up */
1090  clear_line();
1091  Prompt = old_prompt;
1092  TTYputs((ECHAR *)Prompt);
1093  wfree(nprompt);
1094 
1095  kill_line();
1096  s = do_insert_hist((ECHAR *)hist);
1097  if (patend != 0)
1098  for (i=strlen((char *)H.Lines[lpos]); i>cpos; i--) s = bk_char();
1099  if (c != ESC)
1100  return emacs(c);
1101  else
1102  return s;
1103 }
1104 
1105 STATIC STATUS insert_char(int c)
1106 {
1107  STATUS s;
1108  ECHAR buff[2];
1109  ECHAR *p;
1110  ECHAR *q;
1111  int i;
1112 
1113  if (Repeat == NO_ARG || Repeat < 2) {
1114  buff[0] = c;
1115  buff[1] = '\0';
1116  return insert_string(buff);
1117  }
1118 
1119  if ((p = NEW(ECHAR, Repeat + 1)) == NULL)
1120  return CSstay;
1121  for (i = Repeat, q = p; --i >= 0; )
1122  *q++ = c;
1123  *q = '\0';
1124  Repeat = 0;
1125  s = insert_string(p);
1126  DISPOSE(p);
1127  return s;
1128 }
1129 
1130 STATIC STATUS meta()
1131 {
1132  unsigned int c;
1133  KEYMAP *kp;
1134 
1135  if ((c = TTYget()) == EOF)
1136  return CSeof;
1137 #if defined(ANSI_ARROWS)
1138  /* Also include VT-100 arrows. */
1139  if (c == '[' || c == 'O')
1140  switch (c = TTYget()) {
1141  default: return ring_bell();
1142  case EOF: return CSeof;
1143  case 'A': return h_prev();
1144  case 'B': return h_next();
1145  case 'C': return fd_char();
1146  case 'D': return bk_char();
1147  }
1148 #endif /* defined(ANSI_ARROWS) */
1149 
1150  if (isdigit(c)) {
1151  for (Repeat = c - '0'; (c = TTYget()) != EOF && isdigit(c); )
1152  Repeat = Repeat * 10 + c - '0';
1153  el_Pushed = 1;
1154  el_PushBack = c;
1155  return CSstay;
1156  }
1157 
1158 /* if (isupper(c))
1159  return do_macro(c); */
1160  for (OldPoint = Point, kp = MetaMap; kp->Function; kp++)
1161  if (kp->Key == c)
1162  return (*kp->Function)();
1163  if (rl_meta_chars == 0)
1164  {
1165  insert_char(META(c));
1166  return CSmove;
1167  }
1168 
1169  return ring_bell();
1170 }
1171 
1172 STATIC STATUS emacs(unsigned int c)
1173 {
1174  STATUS s;
1175  KEYMAP *kp;
1176 
1177  if (ISMETA(c) && rl_meta_chars)
1178  {
1179  el_Pushed = 1;
1180  el_PushBack = UNMETA(c);
1181  return meta();
1182  }
1183  for (kp = Map; kp->Function; kp++)
1184  if (kp->Key == c)
1185  break;
1186  s = kp->Function ? (*kp->Function)() : insert_char((int)c);
1187  if (!el_Pushed)
1188  /* No pushback means no repeat count; hacky, but true. */
1189  Repeat = NO_ARG;
1190  return s;
1191 }
1192 
1193 STATIC STATUS TTYspecial(unsigned int c)
1194 {
1195  int i;
1196 
1197  if (ISMETA(c))
1198  return CSdispatch;
1199 
1200  if (c == rl_erase || c == DEL)
1201  return bk_del_char();
1202  if (c == rl_kill) {
1203  if (Point != 0) {
1204  for (i=screen_pos()/TTYwidth; i > 0; i--)
1205  if (upline) TTYputs(upline);
1206  Point = 0;
1207  reposition(1);
1208  }
1209  Repeat = NO_ARG;
1210  return kill_line();
1211  }
1212  if (c == rl_intr || c == rl_quit) {
1213  Point = End = 0;
1214  Line[0] = '\0';
1215  if (c == rl_intr)
1216  {
1217  el_intr_pending = 1;
1218  return CSdone;
1219  }
1220  else
1221  return redisplay();
1222  }
1223  if (c == rl_eof && Point == 0 && End == 0)
1224  return CSeof;
1225 
1226  return CSdispatch;
1227 }
1228 
1229 STATIC ECHAR *editinput()
1230 {
1231  unsigned int c;
1232 
1233  Repeat = NO_ARG;
1234  OldPoint = Point = Mark = End = 0;
1235  Line[0] = '\0';
1236 
1237  while ((c = TTYget()) != EOF)
1238  {
1239  switch (TTYspecial(c)) {
1240  case CSdone:
1241  return Line;
1242  case CSeof:
1243  return NULL;
1244  case CSmove:
1245  reposition(1);
1246  break;
1247  case CSstay:
1248  break;
1249  case CSdispatch:
1250  switch (emacs(c)) {
1251  case CSdone:
1252  return Line;
1253  case CSeof:
1254  return NULL;
1255  case CSmove:
1256  reposition(1);
1257  break;
1258  case CSstay:
1259  case CSdispatch:
1260  break;
1261  }
1262  break;
1263  }
1264  }
1265  return NULL;
1266 }
1267 
1268 STATIC void hist_add(ECHAR *p)
1269 {
1270  int i;
1271 
1272  if ((p = (ECHAR *)STRDUP((char *)p)) == NULL)
1273  return;
1274  if (H.Size < editline_histsize)
1275  H.Lines[H.Size++] = p;
1276  else {
1277  DISPOSE(H.Lines[0]);
1278  for (i = 0; i < editline_histsize - 1; i++)
1279  H.Lines[i] = H.Lines[i + 1];
1280  H.Lines[i] = p;
1281  }
1282  H.Pos = H.Size - 1;
1283 }
1284 
1285 /* Added by awb 29/12/98 to get saved history file */
1286 void write_history(const char *history_file)
1287 {
1288  FILE *fd;
1289  int i;
1290 
1291  if ((fd = fopen(history_file,"wb")) == NULL)
1292  {
1293  fprintf(stderr,"editline: can't access history file \"%s\"\n",
1294  history_file);
1295  return;
1296  }
1297 
1298  for (i=0; i < H.Size; i++)
1299  fprintf(fd,"%s\n",H.Lines[i]);
1300  fclose(fd);
1301 }
1302 
1303 void read_history(const char *history_file)
1304 {
1305  FILE *fd;
1306  char buff[2048];
1307  int c,i;
1308 
1309  H.Lines = NEW(ECHAR *,editline_histsize);
1310  H.Size = 0;
1311  H.Pos = 0;
1312 
1313  if ((fd = fopen(history_file,"rb")) == NULL)
1314  return; /* doesn't have a history file yet */
1315 
1316  while ((c=getc(fd)) != EOF)
1317  {
1318  ungetc(c,fd);
1319  for (i=0; ((c=getc(fd)) != '\n') && (c != EOF); i++)
1320  if (i < 2047)
1321  buff[i] = c;
1322  buff[i] = '\0';
1323  add_history(buff);
1324  }
1325 
1326  fclose(fd);
1327 }
1328 
1329 /*
1330 ** For compatibility with FSF readline.
1331 */
1332 /* ARGSUSED0 */
1333 void
1334 rl_reset_terminal(char *p)
1335 {
1336 }
1337 
1338 void
1339 rl_initialize()
1340 {
1341 }
1342 
1343 char *readline(CONST char *prompt)
1344 {
1345  ECHAR *line;
1346 
1347  if (Line == NULL) {
1348  Length = MEM_INC;
1349  if ((Line = NEW(ECHAR, Length)) == NULL)
1350  return NULL;
1351  }
1352 
1353  TTYinfo();
1354  rl_ttyset(0);
1355  hist_add(el_NIL);
1356  ScreenSize = SCREEN_INC;
1357  Screen = NEW(char, ScreenSize);
1358  Prompt = prompt ? prompt : (char *)el_NIL;
1359  el_intr_pending = 0;
1360  if (el_no_echo == 1)
1361  {
1362  el_no_echo = 0;
1363  TTYputs((ECHAR *)Prompt);
1364  TTYflush();
1365  el_no_echo = 1;
1366  }
1367  else
1368  TTYputs((ECHAR *)Prompt);
1369  line = editinput();
1370  if (line != NULL) {
1371  line = (ECHAR *)STRDUP((char *)line);
1372  TTYputs((ECHAR *)newline);
1373  TTYflush();
1374  }
1375  rl_ttyset(1);
1376  DISPOSE(Screen);
1377  DISPOSE(H.Lines[--H.Size]);
1378  if (el_intr_pending)
1379  do_user_intr();
1380  return (char *)line;
1381 }
1382 
1383 void
1384 add_history(p)
1385  char *p;
1386 {
1387  if (p == NULL || *p == '\0')
1388  return;
1389 
1390 #if defined(UNIQUE_HISTORY)
1391  if (H.Pos && strcmp(p, H.Lines[H.Pos - 1]) == 0)
1392  return;
1393 #endif /* defined(UNIQUE_HISTORY) */
1394  hist_add((ECHAR *)p);
1395 }
1396 
1397 
1398 STATIC STATUS beg_line()
1399 {
1400  int i;
1401  if (Point) {
1402  for (i=screen_pos()/TTYwidth; i > 0; i--)
1403  if (upline) TTYputs(upline);
1404  Point = 0;
1405  return CSmove;
1406  }
1407  return CSstay;
1408 }
1409 
1410 STATIC STATUS del_char()
1411 {
1412  return delete_string(Repeat == NO_ARG ? 1 : Repeat);
1413 }
1414 
1415 STATIC STATUS end_line()
1416 {
1417  if (Point != End) {
1418  while (Point < End)
1419  {
1420  TTYput(Line[Point]);
1421  Point++;
1422  }
1423  return CSmove;
1424  }
1425  return CSstay;
1426 }
1427 
1428 /*
1429 ** Move back to the beginning of the current word and return an
1430 ** allocated copy of it.
1431 */
1432 STATIC ECHAR *find_word()
1433 {
1434  static char SEPS[] = "#;&|^$=`'{}()<>\n\t ";
1435  ECHAR *p;
1436  ECHAR *new;
1437  ESIZE_T len;
1438 
1439  for (p = &Line[Point]; p > Line && strchr(SEPS, (char)p[-1]) == NULL; p--)
1440  continue;
1441  len = Point - (p - Line) + 1;
1442  if ((new = NEW(ECHAR, len)) == NULL)
1443  return NULL;
1444  COPYFROMTO(new, p, len);
1445  new[len - 1] = '\0';
1446  return new;
1447 }
1448 
1449 void el_redisplay()
1450 {
1451  reposition(0); /* redisplay assuming already on newline */
1452 }
1453 
1454 char *el_current_sym()
1455 {
1456  /* Get current symbol at point -- awb*/
1457  char *symbol = NULL;
1458  int i,j;
1459 
1460  if (End == 0)
1461  return NULL;
1462  if (Point == End)
1463  i=Point-1;
1464  else
1465  i=Point;
1466 
1467  for ( ;
1468  ((i >= 0) &&
1469  (strchr("()' \t\n\r",Line[i]) != NULL));
1470  i--);
1471  /* i will be on final or before final character */
1472  if (i < 0)
1473  return NULL;
1474  /* But if its not at the end of the current symbol move it there */
1475  for (; i < End; i++)
1476  if (strchr("()' \t\n\r\"",Line[i]) != NULL)
1477  break;
1478  for (j=i-1; j >=0; j--)
1479  if (strchr("()' \t\n\r\"",Line[j]) != NULL)
1480  break;
1481 
1482  symbol = walloc(char,i-j);
1483  strncpy(symbol,(char *)&Line[j+1],i-(j+1));
1484  symbol[i-(j+1)] = '\0';
1485 
1486  return symbol;
1487 }
1488 
1489 static char *completion_to_ambiguity(int index,char **possibles)
1490 {
1491  /* Find the string that extends from index in possibles until an */
1492  /* ambiguity is found -- awb */
1493  char *p;
1494  int e,i;
1495  int extending;
1496 
1497  extending = 1;
1498  e = index;
1499 
1500  for ( ; extending; e++)
1501  {
1502  for (i=0; possibles[i] != NULL; i++)
1503  if (possibles[i][e] != possibles[0][e])
1504  {
1505  extending = 0;
1506  e--;
1507  break;
1508  }
1509  }
1510 
1511  if (e==index)
1512  return NULL; /* already at ambiguity */
1513  else
1514  {
1515  p = walloc(char,(e-index)+1);
1516  strncpy(p,possibles[0]+index,e-index);
1517  p[e-index] = '\0';
1518  return p;
1519  }
1520 }
1521 
1522 static char **el_file_completion_function(char * text, int start, int end)
1523 {
1524  /* Interface to editline rl_list_possib which looks up possible */
1525  /* file name completions. */
1526  char *word;
1527  char **matches1;
1528  char **matches2;
1529  int ac,i;
1530 
1531  word = walloc(char,(end-start)+1);
1532  strncpy(word,text+start,end-start);
1533  word[end-start]='\0';
1534 
1535  ac = rl_list_possib(word,&matches1);
1536  wfree(word);
1537  if (ac == 0)
1538  return NULL;
1539  else
1540  {
1541  matches2 = walloc(char *,ac+1);
1542  for (i=0; i < ac; i++)
1543  matches2[i] = matches1[i];
1544  matches2[i] = NULL;
1545  wfree(matches1);
1546  return matches2;
1547  }
1548 }
1549 
1550 STATIC STATUS c_complete()
1551 {
1552  /* Modified by awb 30/12/98 to allow listing of possibles and */
1553  /* a user definable completion method */
1554  char *p;
1555  char *word;
1556  int start;
1557  char **possibles=NULL;
1558  int possiblesc=0;
1559  int started_with_quote = 0;
1560  STATUS s;
1561  int i;
1562 
1563  for (start=Point; start > 0; start--)
1564  if (strchr("()' \t\n\r\"",Line[start-1]) != NULL)
1565  break;
1566  word = walloc(char,(Point-start)+1);
1567  strncpy(word,(char *)(Line+start),Point-start);
1568  word[Point-start]='\0';
1569  if ((start > 0) && (Line[start-1] == '"'))
1570  started_with_quote = 1;
1571 
1572  if (el_user_completion_function)
1573  /* May need to look at previous char so pass in Line */
1574  possibles = el_user_completion_function((char *)Line,start,Point);
1575  if (possibles == NULL)
1576  {
1577  possibles = el_file_completion_function((char *)Line,start,Point);
1578  /* As filename completions only complete the final file name */
1579  /* not the full path we need to set a new start position */
1580  for (start=Point; start > 0; start--)
1581  if (strchr("()' \t\n\r\"/",Line[start-1]) != NULL)
1582  break;
1583  }
1584  if (possibles)
1585  for (possiblesc=0; possibles[possiblesc] != NULL; possiblesc++);
1586 
1587  if ((!possibles) || (possiblesc == 0)) /* none or none at all */
1588  s = ring_bell();
1589  else if (possiblesc == 1) /* a single expansion */
1590  {
1591  p = walloc(char,strlen(possibles[0])-(Point-start)+2);
1592  sprintf(p,"%s ",possibles[0]+(Point-start));
1593  if ((strlen(p) > 1) && (p[strlen(p)-2] == '/'))
1594  p[strlen(p)-1] = '\0';
1595  else if (started_with_quote)
1596  p[strlen(p)-1] = '"';
1597 
1598  s = insert_string((ECHAR *)p);
1599  wfree(p);
1600  }
1601  else if ((p = completion_to_ambiguity(Point-start,possibles)) != NULL)
1602  { /* an expansion to a later ambiguity */
1603  s = insert_string((ECHAR *)p);
1604  wfree(p);
1605  ring_bell();
1606  }
1607  else /* list of possibilities and we can't expand any further */
1608  {
1609  print_columns(possiblesc,possibles); /* display options */
1610  reposition(0); /* display whole line again */
1611  s = CSmove;
1612  }
1613 
1614  for (i=0; possibles && possibles[i] != NULL; i++)
1615  wfree(possibles[i]);
1616  wfree(possibles);
1617  wfree(word);
1618 
1619  return s;
1620 }
1621 
1622 #if 0
1623 /* Original version without automatic listing of possible completions */
1624 STATIC STATUS c_complete_old()
1625 {
1626  ECHAR *p;
1627  ECHAR *word;
1628  int unique;
1629  STATUS s;
1630 
1631  word = find_word();
1632  p = (ECHAR *)rl_complete((char *)word, &unique);
1633  if (word)
1634  DISPOSE(word);
1635  if (p && *p) {
1636  s = insert_string(p);
1637  if (!unique)
1638  (void)ring_bell();
1639  DISPOSE(p);
1640  return s;
1641  }
1642  return ring_bell();
1643 }
1644 #endif
1645 
1646 STATIC STATUS c_possible()
1647 {
1648  ECHAR **av;
1649  ECHAR *word;
1650  int ac;
1651 
1652  word = find_word();
1653  /* The (char ***) ((void *) &av) below is to avoid a warning
1654  * from GCC about casting an unsigned char *** to char ***
1655  */
1656  ac = rl_list_possib((char *)word, (char ***) ((void *) &av));
1657  if (word)
1658  DISPOSE(word);
1659  if (ac) {
1660  print_columns(ac, (char **)av);
1661  reposition(0);
1662  while (--ac >= 0)
1663  DISPOSE(av[ac]);
1664  DISPOSE(av);
1665  return CSmove;
1666  }
1667  return ring_bell();
1668 }
1669 
1670 STATIC STATUS accept_line()
1671 {
1672  Line[End] = '\0';
1673  return CSdone;
1674 }
1675 
1676 #ifdef SYSTEM_IS_WIN32
1677 STATIC STATUS end_of_input()
1678 {
1679  Line[End] = '\0';
1680  return CSeof;
1681 }
1682 #endif
1683 
1684 STATIC STATUS transpose()
1685 {
1686  ECHAR c;
1687 
1688  if (Point) {
1689  if (Point == End)
1690  left(CSmove);
1691  c = Line[Point - 1];
1692  left(CSstay);
1693  Line[Point - 1] = Line[Point];
1694  TTYshow(Line[Point - 1]);
1695  Line[Point++] = c;
1696  TTYshow(c);
1697  }
1698  return CSstay;
1699 }
1700 
1701 STATIC STATUS quote()
1702 {
1703  unsigned int c;
1704 
1705  return (c = TTYget()) == EOF ? CSeof : insert_char((int)c);
1706 }
1707 
1708 STATIC STATUS wipe()
1709 {
1710  int i;
1711 
1712  if (Mark > End)
1713  return ring_bell();
1714 
1715  if (Point > Mark) {
1716  i = Point;
1717  Point = Mark;
1718  Mark = i;
1719  reposition(1);
1720  }
1721 
1722  return delete_string(Mark - Point);
1723 }
1724 
1725 STATIC STATUS mk_set()
1726 {
1727  Mark = Point;
1728  return CSstay;
1729 }
1730 
1731 STATIC STATUS exchange()
1732 {
1733  unsigned int c;
1734 
1735  if ((c = TTYget()) != CTL('X'))
1736  return c == EOF ? CSeof : ring_bell();
1737 
1738  if ((c = Mark) <= End) {
1739  Mark = Point;
1740  Point = c;
1741  return CSmove;
1742  }
1743  return CSstay;
1744 }
1745 
1746 STATIC STATUS yank()
1747 {
1748  if (Yanked && *Yanked)
1749  return insert_string(Yanked);
1750  return CSstay;
1751 }
1752 
1753 STATIC STATUS copy_region()
1754 {
1755  if (Mark > End)
1756  return ring_bell();
1757 
1758  if (Point > Mark)
1759  save_yank(Mark, Point - Mark);
1760  else
1761  save_yank(Point, Mark - Point);
1762 
1763  return CSstay;
1764 }
1765 
1766 STATIC STATUS move_to_char()
1767 {
1768  unsigned int c;
1769  int i;
1770  ECHAR *p;
1771 
1772  if ((c = TTYget()) == EOF)
1773  return CSeof;
1774  for (i = Point + 1, p = &Line[i]; i < End; i++, p++)
1775  if (*p == c) {
1776  Point = i;
1777  return CSmove;
1778  }
1779  return CSstay;
1780 }
1781 
1782 STATIC STATUS fd_word()
1783 {
1784  return do_forward(CSmove);
1785 }
1786 
1787 STATIC STATUS fd_kill_word()
1788 {
1789  int i;
1790  int OP;
1791 
1792  OP = Point;
1793  (void)do_forward(CSmove);
1794  if (OP != Point) {
1795  i = Point - OP;
1796  for ( ; Point > OP; Point --)
1797  TTYback();
1798  return delete_string(i);
1799  }
1800  return CSmove;
1801 }
1802 
1803 STATIC STATUS bk_word()
1804 {
1805  int i;
1806  ECHAR *p;
1807 
1808  i = 0;
1809  do {
1810  for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--)
1811  left(CSmove);
1812 
1813  for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--)
1814  left(CSmove);
1815 
1816  if (Point == 0)
1817  break;
1818  } while (++i < Repeat);
1819 
1820  return CSstay;
1821 }
1822 
1823 STATIC STATUS bk_kill_word()
1824 {
1825  (void)bk_word();
1826  if (OldPoint != Point)
1827  return delete_string(OldPoint - Point);
1828  return CSstay;
1829 }
1830 
1831 STATIC int argify(ECHAR *line, ECHAR ***avp)
1832 {
1833  ECHAR *c;
1834  ECHAR **p;
1835  ECHAR **new;
1836  int ac;
1837  int i;
1838 
1839  i = MEM_INC;
1840  if ((*avp = p = NEW(ECHAR*, i))== NULL)
1841  return 0;
1842 
1843  for (c = line; isspace(*c); c++)
1844  continue;
1845  if (*c == '\n' || *c == '\0')
1846  return 0;
1847 
1848  for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) {
1849  if (isspace(*c)) {
1850  *c++ = '\0';
1851  if (*c && *c != '\n') {
1852  if (ac + 1 == i) {
1853  new = NEW(ECHAR*, i + MEM_INC);
1854  if (new == NULL) {
1855  p[ac] = NULL;
1856  return ac;
1857  }
1858  COPYFROMTO(new, p, i * sizeof (char **));
1859  i += MEM_INC;
1860  DISPOSE(p);
1861  *avp = p = new;
1862  }
1863  p[ac++] = c;
1864  }
1865  }
1866  else
1867  c++;
1868  }
1869  *c = '\0';
1870  p[ac] = NULL;
1871  return ac;
1872 }
1873 
1874 STATIC STATUS last_argument()
1875 {
1876  ECHAR **av;
1877  ECHAR *p;
1878  STATUS s;
1879  int ac;
1880 
1881  if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL)
1882  return ring_bell();
1883 
1884  if ((p = (ECHAR *)STRDUP((char *)p)) == NULL)
1885  return CSstay;
1886  ac = argify(p, &av);
1887 
1888  if (Repeat != NO_ARG)
1889  s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell();
1890  else
1891  s = ac ? insert_string(av[ac - 1]) : CSstay;
1892 
1893  if (ac)
1894  DISPOSE(av);
1895  DISPOSE(p);
1896  return s;
1897 }
1898 
1899 STATIC KEYMAP Map[33] = {
1900  { CTL('@'), ring_bell },
1901  { CTL('A'), beg_line },
1902  { CTL('B'), bk_char },
1903  { CTL('D'), del_char },
1904  { CTL('E'), end_line },
1905  { CTL('F'), fd_char },
1906  { CTL('G'), ring_bell },
1907  { CTL('H'), bk_del_char },
1908  { CTL('I'), c_complete },
1909  { CTL('J'), accept_line },
1910  { CTL('K'), kill_line },
1911  { CTL('L'), redisplay },
1912  { CTL('M'), accept_line },
1913  { CTL('N'), h_next },
1914  { CTL('O'), ring_bell },
1915  { CTL('P'), h_prev },
1916  { CTL('Q'), ring_bell },
1917  { CTL('R'), h_risearch },
1918  { CTL('S'), h_search },
1919  { CTL('T'), transpose },
1920  { CTL('U'), ring_bell },
1921  { CTL('V'), quote },
1922  { CTL('W'), wipe },
1923  { CTL('X'), exchange },
1924  { CTL('Y'), yank },
1925 #ifdef SYSTEM_IS_WIN32
1926  { CTL('Z'), end_of_input },
1927 #else
1928  { CTL('Z'), ring_bell },
1929 #endif
1930  { CTL('['), meta },
1931  { CTL(']'), move_to_char },
1932  { CTL('^'), ring_bell },
1933  { CTL('_'), ring_bell },
1934  { 0, NULL }
1935 };
1936 
1937 STATIC KEYMAP MetaMap[64]= {
1938  { CTL('H'), bk_kill_word },
1939  { DEL, bk_kill_word },
1940  { ' ', mk_set },
1941  { '.', last_argument },
1942  { '<', h_first },
1943  { '>', h_last },
1944  { '?', c_possible },
1945  { 'b', bk_word },
1946  { 'c', case_cap_word },
1947  { 'd', fd_kill_word },
1948  { 'f', fd_word },
1949  { 'l', case_down_word },
1950  { 'u', case_up_word },
1951  { 'y', yank },
1952  { 'w', copy_region },
1953  { 0, NULL }
1954 };
1955 
1956 void el_bind_key_in_metamap(char c, Keymap_Function func)
1957 {
1958  /* Add given function to key map for META keys */
1959  int i;
1960 
1961  for (i=0; MetaMap[i].Key != 0; i++)
1962  {
1963  if (MetaMap[i].Key == c)
1964  {
1965  MetaMap[i].Function = func;
1966  return;
1967  }
1968  }
1969 
1970  /* A new key so have to add it to end */
1971  if (i == 63)
1972  {
1973  fprintf(stderr,"editline: MetaMap table full, requires increase\n");
1974  return;
1975  }
1976 
1977  MetaMap[i].Function = func;
1978  MetaMap[i].Key = c;
1979  MetaMap[i+1].Function = 0; /* Zero the last location */
1980  MetaMap[i+1].Key = 0; /* Zero the last location */
1981 
1982 }
1983 
1984