readline.c revision 1.1 1 /* $NetBSD: readline.c,v 1.1 1997/10/23 22:24:12 christos Exp $ */
2
3 /*-
4 * Copyright (c) 1997 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jaromir Dolecek.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 #if !defined(lint) && !defined(SCCSID)
41 __RCSID("$NetBSD: readline.c,v 1.1 1997/10/23 22:24:12 christos Exp $");
42 #endif /* not lint && not SCCSID */
43
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <stdio.h>
47 #include <dirent.h>
48 #include <string.h>
49 #include <pwd.h>
50 #include <ctype.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <limits.h>
54 #include "histedit.h"
55 #include "readline.h"
56 #include "sys.h"
57 #include "el.h"
58
59 /* for rl_complete() */
60 #define TAB '\r'
61
62 /* see comment at the #ifdef for sense of this */
63 #define GDB_411_HACK
64
65 /* readline compatibility stuff - look at readline sources/documentation */
66 /* to see what these variables mean */
67 const char *rl_library_version = "EditLine wrapper";
68 char *rl_readline_name = "";
69 FILE *rl_instream = NULL;
70 FILE *rl_outstream = NULL;
71 int rl_point = 0;
72 int rl_end = 0;
73 char *rl_line_buffer = NULL;
74
75 int history_base = 1; /* probably never subject to change */
76 int history_length = 0;
77 int max_input_history = 0;
78 char history_expansion_char = '!';
79 char history_subst_char = '^';
80 char *history_no_expand_chars = " \t\n=(";
81 Function *history_inhibit_expansion_function = NULL;
82
83 int rl_inhibit_completion = 0;
84 int rl_attempted_completion_over = 0;
85 char *rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(";
86 char *rl_completer_word_break_characters = NULL;
87 char *rl_completer_quote_characters = NULL;
88 CPFunction *rl_completion_entry_function = NULL;
89 CPPFunction *rl_attempted_completion_function = NULL;
90
91 /* used for readline emulation */
92 static History *h = NULL;
93 static EditLine *e = NULL;
94
95 /* internal functions */
96 static unsigned char _el_rl_complete __P((EditLine *, int));
97 static char *_get_prompt __P((EditLine *));
98 static const HIST_ENTRY *_move_history __P((int));
99 static int _history_search_gen __P((const char *, int, int));
100 static int _history_expand_command __P((const char *, int, char **));
101 static char *_rl_compat_sub __P((const char *, const char *,
102 const char *, int));
103 static int rl_complete_internal __P((int));
104
105 /*
106 * needed for easy prompt switching
107 */
108 static char *el_rl_prompt = NULL;
109 static char *
110 _get_prompt(el)
111 EditLine *el;
112 {
113 return el_rl_prompt;
114 }
115
116 /*
117 * generic function for moving around history
118 */
119 static const HIST_ENTRY *
120 _move_history(op)
121 int op;
122 {
123 HistEvent ev;
124 static HIST_ENTRY rl_he;
125
126 if (history(h, &ev, op) != 0)
127 return (HIST_ENTRY *) NULL;
128
129 rl_he.line = ev.str;
130 rl_he.data = "";
131
132 return &rl_he;
133 }
134
135
136 /*
137 * READLINE compatibility stuff
138 */
139
140 /*
141 * initialize rl compat stuff
142 */
143 int
144 rl_initialize()
145 {
146 HistEvent ev;
147 const LineInfo *li;
148
149 if (e != NULL)
150 el_end(e);
151 if (h != NULL)
152 history_end(h);
153
154 if (!rl_instream)
155 rl_instream = stdin;
156 if (!rl_outstream)
157 rl_outstream = stdout;
158 e = el_init(rl_readline_name, rl_instream, rl_outstream);
159
160 h = history_init();
161 if (!e || !h)
162 return -1;
163
164 history(h, &ev, H_SETMAXSIZE, INT_MAX); /* unlimited */
165 history_length = 0;
166 max_input_history = INT_MAX;
167 el_set(e, EL_HIST, history, h);
168
169 /* for proper prompt printing in readline() */
170 el_rl_prompt = strdup("");
171 el_set(e, EL_PROMPT, _get_prompt);
172 el_set(e, EL_SIGNAL, 1);
173
174 /* set default mode to "emacs"-style and read setting afterwards */
175 /* so this can be overriden */
176 el_set(e, EL_EDITOR, "emacs");
177
178 /* for word completition - this has to go AFTER rebinding keys */
179 /* to emacs-style */
180 el_set(e, EL_ADDFN, "rl_complete",
181 "ReadLine compatible completition function",
182 _el_rl_complete);
183 el_set(e, EL_BIND, "^I", "rl_complete", NULL);
184
185 /* read settings from configuration file */
186 el_source(e, NULL);
187
188 /* some readline apps do use this */
189 li = el_line(e);
190 rl_line_buffer = (char *) li->buffer;
191 rl_point = rl_end = 0;
192
193 return 0;
194 }
195
196 /*
197 * read one line from input stream and return it, chomping
198 * trailing newline (if there is any)
199 */
200 char *
201 readline(const char *prompt)
202 {
203 HistEvent ev;
204 size_t count;
205 const char *ret;
206
207 if (e == NULL || h == NULL)
208 rl_initialize();
209
210 /* set the prompt */
211 if (strcmp(el_rl_prompt, prompt) != 0) {
212 free(el_rl_prompt);
213 el_rl_prompt = strdup(prompt);
214 }
215 /* get one line from input stream */
216 ret = el_gets(e, &count);
217
218 if (ret && count > 0) {
219 char *foo;
220 off_t lastidx;
221
222 foo = strdup(ret);
223 lastidx = count - 1;
224 if (foo[lastidx] == '\n')
225 foo[lastidx] = '\0';
226
227 ret = foo;
228 } else
229 ret = NULL;
230
231 history(h, &ev, H_GETSIZE);
232 history_length = ev.num;
233
234 return (char *) ret;
235 }
236
237 /*
238 * history functions
239 */
240
241 /*
242 * is normally called before application starts to use
243 * history expansion functions
244 */
245 void
246 using_history()
247 {
248 if (h == NULL || e == NULL)
249 rl_initialize();
250 }
251
252 /*
253 * substitute ``what'' with ``with'', returning resulting string; if
254 * globally == 1, substitutes all occurences of what, otherwise only the
255 * first one
256 */
257 static char *
258 _rl_compat_sub(str, what, with, globally)
259 const char *str, *what, *with;
260 int globally;
261 {
262 char *result;
263 const char *temp, *new;
264 int size, len, i, with_len, what_len, add;
265
266 result = malloc((size = 16));
267 temp = str;
268 with_len = strlen(with);
269 what_len = strlen(what);
270 len = 0;
271 do {
272 new = strstr(temp, what);
273 if (new) {
274 i = new - temp;
275 add = i + with_len;
276 if (i + add + 1 >= size) {
277 size += add + 1;
278 result = realloc(result, size);
279 }
280 (void)strncpy(&result[len], temp, i);
281 len += i;
282 (void)strcpy(&result[len], with); /* safe */
283 len += with_len;
284 temp = new + what_len;
285 } else {
286 add = strlen(temp);
287 if (len + add + 1 >= size) {
288 size += add + 1;
289 result = realloc(result, size);
290 }
291 (void)strcpy(&result[len], temp); /* safe */
292 len += add;
293 temp = NULL;
294 }
295 } while (temp);
296 result[len] = '\0';
297
298 return result;
299 }
300
301 /*
302 * the real function doing history expansion - takes as argument command
303 * to do and data upon which the command should be executed
304 * does expansion the way I've understood readline documentation
305 * word designator ``%'' isn't supported (yet ?)
306 *
307 * returns 0 if data was not modified, 1 if it was and 2 if the string
308 * should be only printed and not executed; in case of error,
309 * returns -1 and *result points to NULL
310 * it's callers responsibility to free() string returned in *result
311 */
312 static int
313 _history_expand_command(command, len, result)
314 const char *command;
315 int len;
316 char **result;
317 {
318 char **arr, *temp, *line, *search = NULL, *cmd;
319 const char *event_data = NULL;
320 static const char *from = NULL, *to = NULL;
321 int start = -1, end = -1, max, i, size, idx;
322 int h_on = 0, t_on = 0, r_on = 0, e_on = 0, p_on = 0,
323 g_on = 0;
324 int event_num = 0, retval;
325
326 *result = NULL;
327
328 cmd = alloca(len + 1);
329 (void)strncpy(cmd, command, len);
330 cmd[len] = 0;
331
332 idx = 1;
333 /* find out which event to take */
334 if (cmd[idx] == history_expansion_char) {
335 event_num = history_length;
336 idx++;
337 } else {
338 int off, len, num;
339 off = idx;
340 while (cmd[off] && !strchr(":^$*-%", cmd[off]))
341 off++;
342 num = atoi(&cmd[idx]);
343 if (num != 0) {
344 event_num = num;
345 if (num < 0)
346 event_num += history_length + 1;
347 } else {
348 int prefix = 1, curr_num;
349 HistEvent ev;
350
351 len = off - idx;
352 if (cmd[idx] == '?') {
353 idx++, len--;
354 if (cmd[off - 1] == '?')
355 len--;
356 else if (cmd[off] != '\n' && cmd[off] != '\0')
357 return -1;
358 prefix = 0;
359 }
360 search = alloca(len + 1);
361 (void)strncpy(search, &cmd[idx], len);
362 search[len] = '\0';
363
364 if (history(h, &ev, H_CURR) != 0)
365 return -1;
366 curr_num = ev.num;
367
368 if (prefix)
369 retval = history_search_prefix(search, -1);
370 else
371 retval = history_search(search, -1);
372
373 if (retval == -1) {
374 fprintf(rl_outstream, "%s: Event not found\n",
375 search);
376 return -1;
377 }
378 if (history(h, &ev, H_CURR) != 0)
379 return -1;
380 event_data = ev.str;
381
382 /* roll back to original position */
383 history(h, &ev, H_NEXT_EVENT, curr_num);
384 }
385 idx = off;
386 }
387
388 if (!event_data && event_num >= 0) {
389 const HIST_ENTRY *rl_he;
390 rl_he = history_get(event_num);
391 if (!rl_he)
392 return 0;
393 event_data = rl_he->line;
394 } else
395 return -1;
396
397 if (cmd[idx] != ':')
398 return -1;
399 cmd += idx + 1;
400
401 /* recognize cmd */
402 if (*cmd == '^')
403 start = end = 1, cmd++;
404 else if (*cmd == '$')
405 start = end = -1, cmd++;
406 else if (*cmd == '*')
407 start = 1, end = -1, cmd++;
408 else if (isdigit(*cmd)) {
409 const char *temp;
410 int shifted = 0;
411
412 start = atoi(cmd);
413 temp = cmd;
414 for (; isdigit(*cmd); cmd++);
415 if (temp != cmd)
416 shifted = 1;
417 if (shifted && *cmd == '-') {
418 if (!isdigit(*(cmd + 1)))
419 end = -2;
420 else {
421 end = atoi(cmd + 1);
422 for (; isdigit(*cmd); cmd++);
423 }
424 } else if (shifted && *cmd == '*')
425 end = -1, cmd++;
426 else if (shifted)
427 end = start;
428 }
429 if (*cmd == ':')
430 cmd++;
431
432 line = strdup(event_data);
433 for (; *cmd; cmd++) {
434 if (*cmd == ':')
435 continue;
436 else if (*cmd == 'h')
437 h_on = 1 | g_on, g_on = 0;
438 else if (*cmd == 't')
439 t_on = 1 | g_on, g_on = 0;
440 else if (*cmd == 'r')
441 r_on = 1 | g_on, g_on = 0;
442 else if (*cmd == 'e')
443 e_on = 1 | g_on, g_on = 0;
444 else if (*cmd == 'p')
445 p_on = 1 | g_on, g_on = 0;
446 else if (*cmd == 'g')
447 g_on = 2;
448 else if (*cmd == 's' || *cmd == '&') {
449 char *what, *with, delim;
450 int len, size, from_len;
451
452 if (*cmd == '&' && (from == NULL || to == NULL))
453 continue;
454 else if (*cmd == 's') {
455 delim = *(++cmd), cmd++;
456 size = 16;
457 what = realloc((void *) from, size);
458 len = 0;
459 for (; *cmd && *cmd != delim; cmd++) {
460 if (*cmd == '\\'
461 && *(cmd + 1) == delim)
462 cmd++;
463 if (len >= size)
464 what = realloc(what,
465 (size <<= 1));
466 what[len++] = *cmd;
467 }
468 what[len] = '\0';
469 from = what;
470 if (*what == '\0') {
471 free(what);
472 if (search)
473 from = strdup(search);
474 else {
475 from = NULL;
476 return -1;
477 }
478 }
479 cmd++; /* shift after delim */
480 if (!*cmd)
481 continue;
482
483 size = 16;
484 with = realloc((void *) to, size);
485 len = 0;
486 from_len = strlen(from);
487 for (; *cmd && *cmd != delim; cmd++) {
488 if (len + from_len + 1 >= size) {
489 size += from_len + 1;
490 with = realloc(with, size);
491 }
492 if (*cmd == '&') {
493 /* safe */
494 (void)strcpy(&with[len], from);
495 len += from_len;
496 continue;
497 }
498 if (*cmd == '\\'
499 && (*(cmd + 1) == delim
500 || *(cmd + 1) == '&'))
501 cmd++;
502 with[len++] = *cmd;
503 }
504 with[len] = '\0';
505 to = with;
506
507 temp = _rl_compat_sub(line, from, to,
508 (g_on) ? 1 : 0);
509 free(line);
510 line = temp;
511 g_on = 0;
512 }
513 }
514 }
515
516 arr = history_tokenize(line);
517 free(line); /* no more needed */
518 if (arr && *arr == NULL)
519 free(arr), arr = NULL;
520 if (!arr)
521 return -1;
522
523 /* find out max valid idx to array of array */
524 max = 0;
525 for (i = 0; arr[i]; i++)
526 max++;
527 max--;
528
529 /* set boundaries to something relevant */
530 if (start < 0)
531 start = 1;
532 if (end < 0)
533 end = max - ((end < -1) ? 1 : 0);
534
535 /* check boundaries ... */
536 if (start > max || end > max || start > end)
537 return -1;
538
539 for (i = 0; i <= max; i++) {
540 char *temp;
541 if (h_on && (i == 1 || h_on > 1) &&
542 (temp = strrchr(arr[i], '/')))
543 *(temp + 1) = '\0';
544 if (t_on && (i == 1 || t_on > 1) &&
545 (temp = strrchr(arr[i], '/')))
546 (void)strcpy(arr[i], temp + 1);
547 if (r_on && (i == 1 || r_on > 1) &&
548 (temp = strrchr(arr[i], '.')))
549 *temp = '\0';
550 if (e_on && (i == 1 || e_on > 1) &&
551 (temp = strrchr(arr[i], '.')))
552 (void)strcpy(arr[i], temp);
553 }
554
555 size = 1, len = 0;
556 temp = malloc(size);
557 for (i = start; start <= i && i <= end; i++) {
558 int arr_len;
559
560 arr_len = strlen(arr[i]);
561 if (len + arr_len + 1 >= size) {
562 size += arr_len + 1;
563 temp = realloc(temp, size);
564 }
565 (void)strcpy(&temp[len], arr[i]); /* safe */
566 len += arr_len;
567 temp[len++] = ' '; /* add a space */
568 }
569 while (len > 0 && isspace(temp[len - 1]))
570 len--;
571 temp[len] = '\0';
572
573 *result = temp;
574
575 for (i = 0; i <= max; i++)
576 free(arr[i]);
577 free(arr), arr = (char **) NULL;
578 return (p_on) ? 2 : 1;
579 }
580
581 /*
582 * csh-style history expansion
583 */
584 int
585 history_expand(str, output)
586 char *str;
587 char **output;
588 {
589 int i, retval = 0, size, idx;
590 char *temp, *result;
591
592 if (h == NULL || e == NULL)
593 rl_initialize();
594
595 *output = strdup(str); /* do it early */
596
597 if (str[0] == history_subst_char) {
598 /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */
599 temp = alloca(4 + strlen(str) + 1);
600 temp[0] = temp[1] = history_expansion_char;
601 temp[2] = ':';
602 temp[3] = 's';
603 (void)strcpy(temp + 4, str);
604 str = temp;
605 }
606 #define ADD_STRING(what, len) \
607 { \
608 if (idx + len + 1 > size) \
609 result = realloc(result, (size += len + 1)); \
610 (void)strncpy(&result[idx], what, len); \
611 idx += len; \
612 result[idx] = '\0'; \
613 }
614
615 result = NULL;
616 size = idx = 0;
617 for (i = 0; str[i];) {
618 int start, j, len, loop_again;
619
620 loop_again = 1;
621 start = j = i;
622 loop:
623 for (; str[j]; j++) {
624 if (str[j] == '\\' &&
625 str[j + 1] == history_expansion_char) {
626 (void)strcpy(&str[j], &str[j + 1]);
627 continue;
628 }
629 if (!loop_again) {
630 if (str[j] == '?') {
631 while (str[j] && str[++j] != '?');
632 if (str[j] == '?')
633 j++;
634 } else if (isspace(str[j]))
635 break;
636 }
637 if (str[j] == history_expansion_char
638 && !strchr(history_no_expand_chars, str[j + 1])
639 && (!history_inhibit_expansion_function ||
640 (*history_inhibit_expansion_function) (str, j) == 0))
641 break;
642 }
643
644 if (str[j] && str[j + 1] != '#' && loop_again) {
645 i = j;
646 j++;
647 if (str[j] == history_expansion_char)
648 j++;
649 loop_again = 0;
650 goto loop;
651 }
652 len = i - start;
653 temp = &str[start];
654 ADD_STRING(temp, len);
655
656 if (str[i] == '\0' || str[i] != history_expansion_char
657 || str[i + 1] == '#') {
658 len = j - i;
659 temp = &str[i];
660 ADD_STRING(temp, len);
661 if (start == 0)
662 retval = 0;
663 else
664 retval = 1;
665 break;
666 }
667 retval = _history_expand_command(&str[i], j - i, &temp);
668 if (retval != -1) {
669 len = strlen(temp);
670 ADD_STRING(temp, len);
671 }
672 i = j;
673 } /* for(i ...) */
674
675 if (retval == 2) {
676 add_history(temp);
677 #ifdef GDB_411_HACK
678 /* gdb 4.11 has been shipped with readline, where */
679 /* history_expand() returned -1 when the line */
680 /* should not be executed; in readline 2.1+ */
681 /* it should return 2 in such a case */
682 retval = -1;
683 #endif
684 }
685 free(*output);
686 *output = result;
687
688 return retval;
689 }
690
691 /*
692 * returns array of tokens parsed out of string, much as the shell might
693 */
694 char **
695 history_tokenize(str)
696 const char *str;
697 {
698 int size = 1, result_idx = 0, i, start, len;
699 char **result = NULL, *temp, delim = '\0';
700
701 for (i = 0; str[i]; i++) {
702 while (isspace(str[i]))
703 i++;
704 start = i;
705 for (; str[i]; i++) {
706 if (str[i] == '\\')
707 i++;
708 else if (str[i] == delim)
709 delim = '\0';
710 else if (!delim &&
711 (isspace(str[i]) || strchr("()<>;&|$", str[i])))
712 break;
713 else if (!delim && strchr("'`\"", str[i]))
714 delim = str[i];
715 }
716
717 if (result_idx + 2 >= size) {
718 size <<= 1;
719 result = realloc(result, size * sizeof(char *));
720 }
721 len = i - start;
722 temp = malloc(len + 1);
723 (void)strncpy(temp, &str[start], len);
724 temp[len] = '\0';
725 result[result_idx++] = temp;
726 result[result_idx] = NULL;
727 }
728
729 return result;
730 }
731
732 /*
733 * limit size of history record to ``max'' events
734 */
735 void
736 stifle_history(max)
737 int max;
738 {
739 HistEvent ev;
740
741 if (h == NULL || e == NULL)
742 rl_initialize();
743
744 if (history(h, &ev, H_SETMAXSIZE, max) == 0)
745 max_input_history = max;
746 }
747
748 /*
749 * "unlimit" size of history - set the limit to maximum allowed int value
750 */
751 int
752 unstifle_history()
753 {
754 HistEvent ev;
755 int omax;
756
757 history(h, &ev, H_SETMAXSIZE, INT_MAX);
758 omax = max_input_history;
759 max_input_history = INT_MAX;
760 return omax; /* some value _must_ be returned */
761 }
762
763 int
764 history_is_stifled()
765 {
766 /* cannot return true answer */
767 return (max_input_history != INT_MAX);
768 }
769
770 /*
771 * read history from a file given
772 */
773 int
774 read_history(filename)
775 const char *filename;
776 {
777 HistEvent ev;
778
779 if (h == NULL || e == NULL)
780 rl_initialize();
781 return history(h, &ev, H_LOAD, filename);
782 }
783
784 /*
785 * write history to a file given
786 */
787 int
788 write_history(filename)
789 const char *filename;
790 {
791 HistEvent ev;
792
793 if (h == NULL || e == NULL)
794 rl_initialize();
795 return history(h, &ev, H_SAVE, filename);
796 }
797
798 /*
799 * returns history ``num''th event
800 *
801 * returned pointer points to static variable
802 */
803 const HIST_ENTRY *
804 history_get(num)
805 int num;
806 {
807 static HIST_ENTRY she;
808 HistEvent ev;
809 int i = 1, curr_num;
810
811 if (h == NULL || e == NULL)
812 rl_initialize();
813
814 /* rewind to beginning */
815 if (history(h, &ev, H_CURR) != 0)
816 return NULL;
817 curr_num = ev.num;
818 if (history(h, &ev, H_LAST) != 0)
819 return NULL; /* error */
820 while (i < num && history(h, &ev, H_PREV) == 0)
821 i++;
822 if (i != num)
823 return NULL; /* not so many entries */
824
825 she.line = ev.str;
826 she.data = NULL;
827
828 /* rewind history to the same event it was before */
829 (void) history(h, &ev, H_FIRST);
830 (void) history(h, &ev, H_NEXT_EVENT, curr_num);
831
832 return &she;
833 }
834
835 /*
836 * add the line to history table
837 */
838 int
839 add_history(line)
840 const char *line;
841 {
842 HistEvent ev;
843
844 if (h == NULL || e == NULL)
845 rl_initialize();
846
847 (void) history(h, &ev, H_ENTER, line);
848 if (history(h, &ev, H_GETSIZE) == 0)
849 history_length = ev.num;
850
851 return (!(history_length > 0)); /* return 0 if all is okay */
852 }
853
854 /*
855 * clear the history list - delete all entries
856 */
857 void
858 clear_history()
859 {
860 HistEvent ev;
861 history(h, &ev, H_CLEAR);
862 }
863
864 /*
865 * returns offset of the current history event
866 */
867 int
868 where_history()
869 {
870 HistEvent ev;
871 int curr_num, off;
872
873 if (history(h, &ev, H_CURR) != 0)
874 return 0;
875 curr_num = ev.num;
876
877 history(h, &ev, H_FIRST);
878 off = 1;
879 while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0)
880 off++;
881
882 return off;
883 }
884
885 /*
886 * returns current history event or NULL if there is no such event
887 */
888 const HIST_ENTRY *
889 current_history()
890 {
891 return _move_history(H_CURR);
892 }
893
894 /*
895 * returns total number of bytes history events' data are using
896 */
897 int
898 history_total_bytes()
899 {
900 HistEvent ev;
901 int curr_num, size;
902
903 if (history(h, &ev, H_CURR) != 0)
904 return -1;
905 curr_num = ev.num;
906
907 history(h, &ev, H_FIRST);
908 size = 0;
909 do
910 size += strlen(ev.str);
911 while (history(h, &ev, H_NEXT) == 0);
912
913 /* get to the same position as before */
914 history(h, &ev, H_PREV_EVENT, curr_num);
915
916 return size;
917 }
918
919 /*
920 * sets the position in the history list to ``pos''
921 */
922 int
923 history_set_pos(pos)
924 int pos;
925 {
926 HistEvent ev;
927 int off, curr_num;
928
929 if (pos > history_length || pos < 0)
930 return -1;
931
932 history(h, &ev, H_CURR);
933 curr_num = ev.num;
934 history(h, &ev, H_FIRST);
935 off = 0;
936 while (off < pos && history(h, &ev, H_NEXT) == 0)
937 off++;
938
939 if (off != pos) { /* do a rollback in case of error */
940 history(h, &ev, H_FIRST);
941 history(h, &ev, H_NEXT_EVENT, curr_num);
942 return -1;
943 }
944 return 0;
945 }
946
947 /*
948 * returns previous event in history and shifts pointer accordingly
949 */
950 const HIST_ENTRY *
951 previous_history()
952 {
953 return _move_history(H_PREV);
954 }
955
956 /*
957 * returns next event in history and shifts pointer accordingly
958 */
959 const HIST_ENTRY *
960 next_history()
961 {
962 return _move_history(H_NEXT);
963 }
964
965 /*
966 * generic history search function
967 */
968 static int
969 _history_search_gen(str, direction, pos)
970 const char *str;
971 int direction, pos;
972 {
973 HistEvent ev;
974 const char *strp;
975 int curr_num;
976
977 if (history(h, &ev, H_CURR) != 0)
978 return -1;
979 curr_num = ev.num;
980
981 for (;;) {
982 strp = strstr(ev.str, str);
983 if (strp && (pos < 0 || &ev.str[pos] == strp))
984 return (int) (strp - ev.str);
985 if (history(h, &ev, direction < 0 ? H_PREV : H_NEXT) != 0)
986 break;
987 }
988
989 history(h, &ev, direction < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
990
991 return -1;
992 }
993
994 /*
995 * searches for first history event containing the str
996 */
997 int
998 history_search(str, direction)
999 const char *str;
1000 int direction;
1001 {
1002 return _history_search_gen(str, direction, -1);
1003 }
1004
1005 /*
1006 * searches for first history event beginning with str
1007 */
1008 int
1009 history_search_prefix(str, direction)
1010 const char *str;
1011 int direction;
1012 {
1013 return _history_search_gen(str, direction, 0);
1014 }
1015
1016 /*
1017 * search for event in history containing str, starting at offset
1018 * abs(pos); continue backward, if pos<0, forward otherwise
1019 */
1020 int
1021 history_search_pos(str, direction, pos)
1022 const char *str;
1023 int direction, pos;
1024 {
1025 HistEvent ev;
1026 int curr_num, off;
1027
1028 off = (pos > 0) ? pos : -pos;
1029 pos = (pos > 0) ? 1 : -1;
1030
1031 if (history(h, &ev, H_CURR) != 0)
1032 return -1;
1033 curr_num = ev.num;
1034
1035 if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0)
1036 return -1;
1037
1038
1039 for (;;) {
1040 if (strstr(ev.str, str))
1041 return off;
1042 if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)
1043 break;
1044 }
1045
1046 /* set "current" pointer back to previous state */
1047 history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
1048
1049 return -1;
1050 }
1051
1052
1053 /********************************/
1054 /* completition functions */
1055
1056 /*
1057 * does tilde expansion of strings of type ``~user/foo''
1058 * if ``user'' isn't valid user name or ``txt'' doesn't start
1059 * w/ '~', returns pointer to strdup()ed copy of ``txt''
1060 *
1061 * it's callers's responsibility to free() returned string
1062 */
1063 char *
1064 tilde_expand(txt)
1065 const char *txt;
1066 {
1067 struct passwd *pass;
1068 char *temp;
1069 size_t len = 0;
1070
1071 if (txt[0] != '~')
1072 return strdup(txt);
1073
1074 temp = strchr(txt + 1, '/');
1075 if (temp == NULL)
1076 temp = strdup(txt + 1);
1077 else {
1078 len = temp - txt + 1; /* text until string after slash */
1079 temp = malloc(len);
1080 (void)strncpy(temp, txt + 1, len - 2);
1081 temp[len - 2] = '\0';
1082 }
1083 pass = getpwnam(temp);
1084 free(temp); /* value no more needed */
1085 if (pass == NULL)
1086 return strdup(txt);
1087
1088 /* update pointer txt to point at string immedially following */
1089 /* first slash */
1090 txt += len;
1091
1092 temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1);
1093 (void)sprintf(temp, "%s/%s", pass->pw_dir, txt);
1094
1095 return temp;
1096 }
1097
1098 /*
1099 * return first found file name starting by the ``text'' or NULL if no
1100 * such file can be found
1101 * value of ``state'' is ignored
1102 *
1103 * it's caller's responsibility to free returned string
1104 */
1105 char *
1106 filename_completion_function(text, state)
1107 const char *text;
1108 int state;
1109 {
1110 static DIR *dir = NULL;
1111 static char *filename = NULL, *dirname = NULL;
1112 static size_t filename_len = 0;
1113 struct dirent *entry;
1114 char *temp;
1115 size_t len;
1116
1117 if (state == 0 || dir == NULL) {
1118 if (dir != NULL) {
1119 closedir(dir);
1120 dir = NULL;
1121 }
1122 temp = strrchr(text, '/');
1123 if (temp) {
1124 temp++;
1125 filename = realloc(filename, strlen(temp) + 1);
1126 (void)strcpy(filename, temp);
1127 len = temp - text; /* including last slash */
1128 dirname = realloc(dirname, len + 1);
1129 (void)strncpy(dirname, text, len);
1130 dirname[len] = '\0';
1131 } else {
1132 filename = strdup(text);
1133 dirname = NULL;
1134 }
1135
1136 /* support for ``~user'' syntax */
1137 if (dirname && *dirname == '~') {
1138 temp = tilde_expand(dirname);
1139 dirname = realloc(dirname, strlen(temp) + 1);
1140 (void)strcpy(dirname, temp); /* safe */
1141 free(temp); /* no more needed */
1142 }
1143 /* will be used in cycle */
1144 filename_len = strlen(filename);
1145 if (filename_len == 0)
1146 return NULL; /* no expansion possible */
1147
1148 dir = opendir(dirname ? dirname : ".");
1149 if (!dir)
1150 return NULL; /* cannot open the directory */
1151 }
1152 /* find the match */
1153 while ((entry = readdir(dir))) {
1154 /* otherwise, get first entry where first */
1155 /* filename_len characters are equal */
1156 if (entry->d_name[0] == filename[0]
1157 && entry->d_namlen >= filename_len
1158 && strncmp(entry->d_name, filename,
1159 filename_len) == 0)
1160 break;
1161 }
1162
1163 if (entry) { /* match found */
1164
1165 struct stat stbuf;
1166 len = entry->d_namlen +
1167 ((dirname) ? strlen(dirname) : 0) + 1 + 1;
1168 temp = malloc(len);
1169 (void)sprintf(temp, "%s%s",
1170 dirname ? dirname : "", entry->d_name); /* safe */
1171
1172 /* test, if it's directory */
1173 if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
1174 strcat(temp, "/"); /* safe */
1175 } else
1176 temp = NULL;
1177
1178 return temp;
1179 }
1180
1181 /*
1182 * a completion generator for usernames; returns _first_ username
1183 * which starts with supplied text
1184 * text contains a partial username preceded by random character
1185 * (usually '~'); state is ignored
1186 * it's callers responsibility to free returned value
1187 */
1188 char *
1189 username_completion_function(text, state)
1190 const char *text;
1191 int state;
1192 {
1193 struct passwd *pwd;
1194
1195 if (text[0] == '\0')
1196 return NULL;
1197
1198 if (*text == '~')
1199 text++;
1200
1201 if (state == 0)
1202 setpwent();
1203
1204 while ((pwd = getpwent()) && text[0] == pwd->pw_name[0]
1205 && strcmp(text, pwd->pw_name) == 0);
1206
1207 if (pwd == NULL) {
1208 endpwent();
1209 return NULL;
1210 }
1211 return strdup(pwd->pw_name);
1212 }
1213
1214 /*
1215 * el-compatible wrapper around rl_complete; needed for key binding
1216 */
1217 static unsigned char
1218 _el_rl_complete(el, ch)
1219 EditLine *el;
1220 int ch;
1221 {
1222 return (unsigned char) rl_complete(0, ch);
1223 }
1224
1225 /*
1226 * returns list of completitions for text given
1227 */
1228 char **
1229 completion_matches(text, genfunc)
1230 const char *text;
1231 CPFunction *genfunc;
1232 {
1233 char **match_list = NULL, *retstr, *prevstr;
1234 size_t matches, math_list_len, max_equal, len, which,
1235 i;
1236
1237 if (h == NULL || e == NULL)
1238 rl_initialize();
1239
1240 matches = 0;
1241 math_list_len = 1;
1242 while ((retstr = (*genfunc) (text, matches))) {
1243 if (matches + 1 >= math_list_len) {
1244 math_list_len <<= 1;
1245 match_list = realloc(match_list,
1246 math_list_len * sizeof(char *));
1247 }
1248 match_list[++matches] = retstr;
1249 }
1250
1251 if (!match_list)
1252 return (char **) NULL; /* nothing found */
1253
1254 /* find least denominator and insert it to match_list[0] */
1255 which = 2;
1256 prevstr = match_list[1];
1257 len = max_equal = strlen(prevstr);
1258 for (; which < matches; which++) {
1259 for (i = 0; i < max_equal &&
1260 prevstr[i] == match_list[which][i]; i++)
1261 continue;
1262 max_equal = i;
1263 }
1264
1265 retstr = malloc(max_equal + 1);
1266 (void)strncpy(retstr, match_list[1], max_equal);
1267 retstr[max_equal] = '\0';
1268 match_list[0] = retstr;
1269
1270 /* add NULL as last pointer to the array */
1271 if (matches + 1 >= math_list_len)
1272 match_list = realloc(match_list,
1273 (math_list_len + 1) * sizeof(char *));
1274 match_list[matches + 1] = (char *) NULL;
1275
1276 return match_list;
1277 }
1278
1279 /*
1280 * called by rl_complete()
1281 */
1282 static int
1283 rl_complete_internal(what_to_do)
1284 int what_to_do;
1285 {
1286 CPFunction *complet_func;
1287 const LineInfo *li;
1288 char *temp, *temp2, **arr;
1289 int len;
1290
1291 if (h == NULL || e == NULL)
1292 rl_initialize();
1293
1294 complet_func = rl_completion_entry_function;
1295 if (!complet_func)
1296 complet_func = filename_completion_function;
1297
1298 li = el_line(e);
1299 temp = (char *) li->cursor;
1300 while (temp > li->buffer &&
1301 !strchr(rl_basic_word_break_characters, *(temp - 1)))
1302 temp--;
1303
1304 len = li->cursor - temp;
1305 temp2 = alloca(len + 1);
1306 (void)strncpy(temp2, temp, len);
1307 temp = temp2;
1308 temp[len] = '\0';
1309
1310 /* these can be used by function called in completion_matches() */
1311 /* or (*rl_attempted_completion_function)() */
1312 rl_point = li->cursor - li->buffer;
1313 rl_end = li->lastchar - li->buffer;
1314
1315 if (!rl_attempted_completion_function)
1316 arr = completion_matches(temp, complet_func);
1317 else {
1318 int end = li->cursor - li->buffer;
1319 arr = (*rl_attempted_completion_function) (temp,
1320 end - len, end);
1321 }
1322 free(temp); /* no more needed */
1323
1324 if (arr) {
1325 int i;
1326
1327 el_deletestr(e, len);
1328 el_insertstr(e, arr[0]);
1329 if (strcmp(arr[0], arr[1]) == 0) {
1330 /* lcd is valid object, so add a space to mark it */
1331 /* in case of filename completition, add a space */
1332 /* only if object found is not directory */
1333 int len = strlen(arr[0]);
1334 if (complet_func != filename_completion_function
1335 || (len > 0 && (arr[0])[len - 1] != '/'))
1336 el_insertstr(e, " ");
1337 } else
1338 /* lcd is not a valid object - further specification */
1339 /* is needed */
1340 term_beep(e);
1341
1342 /* free elements of array and the array itself */
1343 for (i = 0; arr[i]; i++)
1344 free(arr[i]);
1345 free(arr), arr = NULL;
1346
1347 return CC_REFRESH;
1348 }
1349 return CC_NORM;
1350 }
1351
1352 /*
1353 * complete word at current point
1354 */
1355 int
1356 rl_complete(ignore, invoking_key)
1357 int ignore, invoking_key;
1358 {
1359 if (h == NULL || e == NULL)
1360 rl_initialize();
1361
1362 if (rl_inhibit_completion) {
1363 rl_insert(ignore, invoking_key);
1364 return CC_REFRESH;
1365 } else
1366 return rl_complete_internal(invoking_key);
1367
1368 return CC_REFRESH;
1369 }
1370
1371 /*
1372 * misc other functions
1373 */
1374
1375 /*
1376 * bind key c to readline-type function func
1377 */
1378 int
1379 rl_bind_key(c, func)
1380 int c;
1381 int func __P((int, int));
1382 {
1383 int retval = -1;
1384
1385 if (h == NULL || e == NULL)
1386 rl_initialize();
1387
1388 if (func == rl_insert) {
1389 /* XXX notice there is no range checking of ``c'' */
1390 e->el_map.key[c] = ED_INSERT;
1391 retval = 0;
1392 }
1393 return retval;
1394 }
1395
1396 /*
1397 * read one key from input - handles chars pushed back
1398 * to input stream also
1399 */
1400 int
1401 rl_read_key()
1402 {
1403 char fooarr[2 * sizeof(int)];
1404
1405 if (e == NULL || h == NULL)
1406 rl_initialize();
1407
1408 return el_getc(e, fooarr);
1409 }
1410
1411 /*
1412 * reset the terminal
1413 */
1414 void
1415 rl_reset_terminal(p)
1416 const char *p;
1417 {
1418 if (h == NULL || e == NULL)
1419 rl_initialize();
1420 el_reset(e);
1421 }
1422
1423 /*
1424 * insert character ``c'' back into input stream, ``count'' times
1425 */
1426 int
1427 rl_insert(count, c)
1428 int count, c;
1429 {
1430 char arr[2];
1431
1432 if (h == NULL || e == NULL)
1433 rl_initialize();
1434
1435 /* XXX - int -> char conversion can lose on multichars */
1436 arr[0] = c;
1437 arr[1] = '\0';
1438
1439 for (; count > 0; count--)
1440 el_push(e, arr);
1441
1442 return 0;
1443 }
1444