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