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