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