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