readline.c revision 1.10 1 /* $NetBSD: readline.c,v 1.10 2000/03/10 13:06:43 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.10 2000/03/10 13:06:43 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 static char *
264 _rl_compat_sub(str, what, with, globally)
265 const char *str, *what, *with;
266 int globally;
267 {
268 char *result;
269 const char *temp, *new;
270 int len, with_len, what_len, add;
271 size_t size, i;
272
273 result = malloc((size = 16));
274 temp = str;
275 with_len = strlen(with);
276 what_len = strlen(what);
277 len = 0;
278 do {
279 new = strstr(temp, what);
280 if (new) {
281 i = new - temp;
282 add = i + with_len;
283 if (i + add + 1 >= size) {
284 size += add + 1;
285 result = realloc(result, size);
286 }
287 (void)strncpy(&result[len], temp, i);
288 len += i;
289 (void)strcpy(&result[len], with); /* safe */
290 len += with_len;
291 temp = new + what_len;
292 } else {
293 add = strlen(temp);
294 if (len + add + 1 >= size) {
295 size += add + 1;
296 result = realloc(result, size);
297 }
298 (void)strcpy(&result[len], temp); /* safe */
299 len += add;
300 temp = NULL;
301 }
302 } while (temp && globally);
303 result[len] = '\0';
304
305 return result;
306 }
307
308 /*
309 * the real function doing history expansion - takes as argument command
310 * to do and data upon which the command should be executed
311 * does expansion the way I've understood readline documentation
312 * word designator ``%'' isn't supported (yet ?)
313 *
314 * returns 0 if data was not modified, 1 if it was and 2 if the string
315 * should be only printed and not executed; in case of error,
316 * returns -1 and *result points to NULL
317 * it's callers responsibility to free() string returned in *result
318 */
319 static int
320 _history_expand_command(command, cmdlen, result)
321 const char *command;
322 size_t cmdlen;
323 char **result;
324 {
325 char **arr, *tempcmd, *line, *search = NULL, *cmd;
326 const char *event_data = NULL;
327 static char *from = NULL, *to = NULL;
328 int start = -1, end = -1, max, i, idx;
329 int h_on = 0, t_on = 0, r_on = 0, e_on = 0, p_on = 0,
330 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 * csh-style history expansion
593 */
594 int
595 history_expand(str, output)
596 char *str;
597 char **output;
598 {
599 int i, retval = 0, idx;
600 size_t size;
601 char *temp, *result;
602
603 if (h == NULL || e == NULL)
604 rl_initialize();
605
606 *output = strdup(str); /* do it early */
607
608 if (str[0] == history_subst_char) {
609 /* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */
610 temp = alloca(4 + strlen(str) + 1);
611 temp[0] = temp[1] = history_expansion_char;
612 temp[2] = ':';
613 temp[3] = 's';
614 (void)strcpy(temp + 4, str);
615 str = temp;
616 }
617 #define ADD_STRING(what, len) \
618 { \
619 if (idx + len + 1 > size) \
620 result = realloc(result, (size += len + 1)); \
621 (void)strncpy(&result[idx], what, len); \
622 idx += len; \
623 result[idx] = '\0'; \
624 }
625
626 result = NULL;
627 size = idx = 0;
628 for (i = 0; str[i];) {
629 int start, j, loop_again;
630 size_t len;
631
632 loop_again = 1;
633 start = j = i;
634 loop:
635 for (; str[j]; j++) {
636 if (str[j] == '\\' &&
637 str[j + 1] == history_expansion_char) {
638 (void)strcpy(&str[j], &str[j + 1]);
639 continue;
640 }
641 if (!loop_again) {
642 if (str[j] == '?') {
643 while (str[j] && str[++j] != '?');
644 if (str[j] == '?')
645 j++;
646 } else if (isspace((unsigned char) str[j]))
647 break;
648 }
649 if (str[j] == history_expansion_char
650 && !strchr(history_no_expand_chars, str[j + 1])
651 && (!history_inhibit_expansion_function ||
652 (*history_inhibit_expansion_function) (str, j) == 0))
653 break;
654 }
655
656 if (str[j] && str[j + 1] != '#' && loop_again) {
657 i = j;
658 j++;
659 if (str[j] == history_expansion_char)
660 j++;
661 loop_again = 0;
662 goto loop;
663 }
664 len = i - start;
665 temp = &str[start];
666 ADD_STRING(temp, len);
667
668 if (str[i] == '\0' || str[i] != history_expansion_char
669 || str[i + 1] == '#') {
670 len = j - i;
671 temp = &str[i];
672 ADD_STRING(temp, len);
673 if (start == 0)
674 retval = 0;
675 else
676 retval = 1;
677 break;
678 }
679 retval = _history_expand_command(&str[i], (size_t)(j - i),
680 &temp);
681 if (retval != -1) {
682 len = strlen(temp);
683 ADD_STRING(temp, len);
684 }
685 i = j;
686 } /* for(i ...) */
687
688 if (retval == 2) {
689 add_history(temp);
690 #ifdef GDB_411_HACK
691 /* gdb 4.11 has been shipped with readline, where */
692 /* history_expand() returned -1 when the line */
693 /* should not be executed; in readline 2.1+ */
694 /* it should return 2 in such a case */
695 retval = -1;
696 #endif
697 }
698 free(*output);
699 *output = result;
700
701 return retval;
702 }
703
704 /*
705 * Parse the string into individual tokens, similarily to how shell would do it.
706 */
707 char **
708 history_tokenize(str)
709 const char *str;
710 {
711 int size = 1, result_idx = 0, i, start;
712 size_t len;
713 char **result = NULL, *temp, delim = '\0';
714
715 for (i = 0; str[i]; i++) {
716 while (isspace((unsigned char) str[i]))
717 i++;
718 start = i;
719 for (; str[i]; i++) {
720 if (str[i] == '\\') {
721 if (str[i] != '\0')
722 i++;
723 } else if (str[i] == delim)
724 delim = '\0';
725 else if (!delim &&
726 (isspace((unsigned char) str[i]) ||
727 strchr("()<>;&|$", str[i])))
728 break;
729 else if (!delim && strchr("'`\"", str[i]))
730 delim = str[i];
731 }
732
733 if (result_idx + 2 >= size) {
734 size <<= 1;
735 result = realloc(result, size * sizeof(char *));
736 }
737 len = i - start;
738 temp = malloc(len + 1);
739 (void)strncpy(temp, &str[start], len);
740 temp[len] = '\0';
741 result[result_idx++] = temp;
742 result[result_idx] = NULL;
743 }
744
745 return result;
746 }
747
748 /*
749 * limit size of history record to ``max'' events
750 */
751 void
752 stifle_history(max)
753 int max;
754 {
755 HistEvent ev;
756
757 if (h == NULL || e == NULL)
758 rl_initialize();
759
760 if (history(h, &ev, H_SETSIZE, max) == 0)
761 max_input_history = max;
762 }
763
764 /*
765 * "unlimit" size of history - set the limit to maximum allowed int value
766 */
767 int
768 unstifle_history()
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 int
780 history_is_stifled()
781 {
782 /* cannot return true answer */
783 return (max_input_history != INT_MAX);
784 }
785
786 /*
787 * read history from a file given
788 */
789 int
790 read_history(filename)
791 const char *filename;
792 {
793 HistEvent ev;
794
795 if (h == NULL || e == NULL)
796 rl_initialize();
797 return history(h, &ev, H_LOAD, filename);
798 }
799
800 /*
801 * write history to a file given
802 */
803 int
804 write_history(filename)
805 const char *filename;
806 {
807 HistEvent ev;
808
809 if (h == NULL || e == NULL)
810 rl_initialize();
811 return history(h, &ev, H_SAVE, filename);
812 }
813
814 /*
815 * returns history ``num''th event
816 *
817 * returned pointer points to static variable
818 */
819 HIST_ENTRY *
820 history_get(num)
821 int num;
822 {
823 static HIST_ENTRY she;
824 HistEvent ev;
825 int i = 1, curr_num;
826
827 if (h == NULL || e == NULL)
828 rl_initialize();
829
830 /* rewind to beginning */
831 if (history(h, &ev, H_CURR) != 0)
832 return NULL;
833 curr_num = ev.num;
834 if (history(h, &ev, H_LAST) != 0)
835 return NULL; /* error */
836 while (i < num && history(h, &ev, H_PREV) == 0)
837 i++;
838 if (i != num)
839 return NULL; /* not so many entries */
840
841 she.line = ev.str;
842 she.data = NULL;
843
844 /* rewind history to the same event it was before */
845 (void) history(h, &ev, H_FIRST);
846 (void) history(h, &ev, H_NEXT_EVENT, curr_num);
847
848 return &she;
849 }
850
851 /*
852 * add the line to history table
853 */
854 int
855 add_history(line)
856 const char *line;
857 {
858 HistEvent ev;
859
860 if (h == NULL || e == NULL)
861 rl_initialize();
862
863 (void) history(h, &ev, H_ENTER, line);
864 if (history(h, &ev, H_GETSIZE) == 0)
865 history_length = ev.num;
866
867 return (!(history_length > 0)); /* return 0 if all is okay */
868 }
869
870 /*
871 * clear the history list - delete all entries
872 */
873 void
874 clear_history()
875 {
876 HistEvent ev;
877 history(h, &ev, H_CLEAR);
878 }
879
880 /*
881 * returns offset of the current history event
882 */
883 int
884 where_history()
885 {
886 HistEvent ev;
887 int curr_num, off;
888
889 if (history(h, &ev, H_CURR) != 0)
890 return 0;
891 curr_num = ev.num;
892
893 history(h, &ev, H_FIRST);
894 off = 1;
895 while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0)
896 off++;
897
898 return off;
899 }
900
901 /*
902 * returns current history event or NULL if there is no such event
903 */
904 HIST_ENTRY *
905 current_history()
906 {
907 return _move_history(H_CURR);
908 }
909
910 /*
911 * returns total number of bytes history events' data are using
912 */
913 int
914 history_total_bytes()
915 {
916 HistEvent ev;
917 int curr_num, size;
918
919 if (history(h, &ev, H_CURR) != 0)
920 return -1;
921 curr_num = ev.num;
922
923 history(h, &ev, H_FIRST);
924 size = 0;
925 do
926 size += strlen(ev.str);
927 while (history(h, &ev, H_NEXT) == 0);
928
929 /* get to the same position as before */
930 history(h, &ev, H_PREV_EVENT, curr_num);
931
932 return size;
933 }
934
935 /*
936 * sets the position in the history list to ``pos''
937 */
938 int
939 history_set_pos(pos)
940 int pos;
941 {
942 HistEvent ev;
943 int off, curr_num;
944
945 if (pos > history_length || pos < 0)
946 return -1;
947
948 history(h, &ev, H_CURR);
949 curr_num = ev.num;
950 history(h, &ev, H_FIRST);
951 off = 0;
952 while (off < pos && history(h, &ev, H_NEXT) == 0)
953 off++;
954
955 if (off != pos) { /* do a rollback in case of error */
956 history(h, &ev, H_FIRST);
957 history(h, &ev, H_NEXT_EVENT, curr_num);
958 return -1;
959 }
960 return 0;
961 }
962
963 /*
964 * returns previous event in history and shifts pointer accordingly
965 */
966 HIST_ENTRY *
967 previous_history()
968 {
969 return _move_history(H_PREV);
970 }
971
972 /*
973 * returns next event in history and shifts pointer accordingly
974 */
975 HIST_ENTRY *
976 next_history()
977 {
978 return _move_history(H_NEXT);
979 }
980
981 /*
982 * generic history search function
983 */
984 static int
985 _history_search_gen(str, direction, pos)
986 const char *str;
987 int direction, pos;
988 {
989 HistEvent ev;
990 const char *strp;
991 int curr_num;
992
993 if (history(h, &ev, H_CURR) != 0)
994 return -1;
995 curr_num = ev.num;
996
997 for (;;) {
998 strp = strstr(ev.str, str);
999 if (strp && (pos < 0 || &ev.str[pos] == strp))
1000 return (int) (strp - ev.str);
1001 if (history(h, &ev, direction < 0 ? H_PREV : H_NEXT) != 0)
1002 break;
1003 }
1004
1005 history(h, &ev, direction < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
1006
1007 return -1;
1008 }
1009
1010 /*
1011 * searches for first history event containing the str
1012 */
1013 int
1014 history_search(str, direction)
1015 const char *str;
1016 int direction;
1017 {
1018 return _history_search_gen(str, direction, -1);
1019 }
1020
1021 /*
1022 * searches for first history event beginning with str
1023 */
1024 int
1025 history_search_prefix(str, direction)
1026 const char *str;
1027 int direction;
1028 {
1029 return _history_search_gen(str, direction, 0);
1030 }
1031
1032 /*
1033 * search for event in history containing str, starting at offset
1034 * abs(pos); continue backward, if pos<0, forward otherwise
1035 */
1036 /* ARGSUSED */
1037 int
1038 history_search_pos(str, direction, pos)
1039 const char *str;
1040 int direction, pos;
1041 {
1042 HistEvent ev;
1043 int curr_num, off;
1044
1045 off = (pos > 0) ? pos : -pos;
1046 pos = (pos > 0) ? 1 : -1;
1047
1048 if (history(h, &ev, H_CURR) != 0)
1049 return -1;
1050 curr_num = ev.num;
1051
1052 if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0)
1053 return -1;
1054
1055
1056 for (;;) {
1057 if (strstr(ev.str, str))
1058 return off;
1059 if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)
1060 break;
1061 }
1062
1063 /* set "current" pointer back to previous state */
1064 history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
1065
1066 return -1;
1067 }
1068
1069
1070 /********************************/
1071 /* completition functions */
1072
1073 /*
1074 * does tilde expansion of strings of type ``~user/foo''
1075 * if ``user'' isn't valid user name or ``txt'' doesn't start
1076 * w/ '~', returns pointer to strdup()ed copy of ``txt''
1077 *
1078 * it's callers's responsibility to free() returned string
1079 */
1080 char *
1081 tilde_expand(txt)
1082 char *txt;
1083 {
1084 struct passwd *pass;
1085 char *temp;
1086 size_t len = 0;
1087
1088 if (txt[0] != '~')
1089 return strdup(txt);
1090
1091 temp = strchr(txt + 1, '/');
1092 if (temp == NULL)
1093 temp = strdup(txt + 1);
1094 else {
1095 len = temp - txt + 1; /* text until string after slash */
1096 temp = malloc(len);
1097 (void)strncpy(temp, txt + 1, len - 2);
1098 temp[len - 2] = '\0';
1099 }
1100 pass = getpwnam(temp);
1101 free(temp); /* value no more needed */
1102 if (pass == NULL)
1103 return strdup(txt);
1104
1105 /* update pointer txt to point at string immedially following */
1106 /* first slash */
1107 txt += len;
1108
1109 temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1);
1110 (void)sprintf(temp, "%s/%s", pass->pw_dir, txt);
1111
1112 return temp;
1113 }
1114
1115 /*
1116 * return first found file name starting by the ``text'' or NULL if no
1117 * such file can be found
1118 * value of ``state'' is ignored
1119 *
1120 * it's caller's responsibility to free returned string
1121 */
1122 char *
1123 filename_completion_function(text, state)
1124 const char *text;
1125 int state;
1126 {
1127 static DIR *dir = NULL;
1128 static char *filename = NULL, *dirname = NULL;
1129 static size_t filename_len = 0;
1130 struct dirent *entry;
1131 char *temp;
1132 size_t len;
1133
1134 if (state == 0 || dir == NULL) {
1135 if (dir != NULL) {
1136 closedir(dir);
1137 dir = NULL;
1138 }
1139 temp = strrchr(text, '/');
1140 if (temp) {
1141 temp++;
1142 filename = realloc(filename, strlen(temp) + 1);
1143 (void)strcpy(filename, temp);
1144 len = temp - text; /* including last slash */
1145 dirname = realloc(dirname, len + 1);
1146 (void)strncpy(dirname, text, len);
1147 dirname[len] = '\0';
1148 } else {
1149 filename = strdup(text);
1150 dirname = NULL;
1151 }
1152
1153 /* support for ``~user'' syntax */
1154 if (dirname && *dirname == '~') {
1155 temp = tilde_expand(dirname);
1156 dirname = realloc(dirname, strlen(temp) + 1);
1157 (void)strcpy(dirname, temp); /* safe */
1158 free(temp); /* no more needed */
1159 }
1160 /* will be used in cycle */
1161 filename_len = strlen(filename);
1162 if (filename_len == 0)
1163 return NULL; /* no expansion possible */
1164
1165 dir = opendir(dirname ? dirname : ".");
1166 if (!dir)
1167 return NULL; /* cannot open the directory */
1168 }
1169 /* find the match */
1170 while ((entry = readdir(dir)) != NULL) {
1171 /* otherwise, get first entry where first */
1172 /* filename_len characters are equal */
1173 if (entry->d_name[0] == filename[0]
1174 #if defined(__SVR4) || defined(__linux__)
1175 && strlen(entry->d_name) >= filename_len
1176 #else
1177 && entry->d_namlen >= filename_len
1178 #endif
1179 && strncmp(entry->d_name, filename,
1180 filename_len) == 0)
1181 break;
1182 }
1183
1184 if (entry) { /* match found */
1185
1186 struct stat stbuf;
1187 #if defined(__SVR4) || defined(__linux__)
1188 len = strlen(entry->d_name) +
1189 #else
1190 len = entry->d_namlen +
1191 #endif
1192 ((dirname) ? strlen(dirname) : 0) + 1 + 1;
1193 temp = malloc(len);
1194 (void)sprintf(temp, "%s%s",
1195 dirname ? dirname : "", entry->d_name); /* safe */
1196
1197 /* test, if it's directory */
1198 if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
1199 strcat(temp, "/"); /* safe */
1200 } else
1201 temp = NULL;
1202
1203 return temp;
1204 }
1205
1206 /*
1207 * a completion generator for usernames; returns _first_ username
1208 * which starts with supplied text
1209 * text contains a partial username preceded by random character
1210 * (usually '~'); state is ignored
1211 * it's callers responsibility to free returned value
1212 */
1213 char *
1214 username_completion_function(text, state)
1215 const char *text;
1216 int state;
1217 {
1218 struct passwd *pwd;
1219
1220 if (text[0] == '\0')
1221 return NULL;
1222
1223 if (*text == '~')
1224 text++;
1225
1226 if (state == 0)
1227 setpwent();
1228
1229 while ((pwd = getpwent()) && text[0] == pwd->pw_name[0]
1230 && strcmp(text, pwd->pw_name) == 0);
1231
1232 if (pwd == NULL) {
1233 endpwent();
1234 return NULL;
1235 }
1236 return strdup(pwd->pw_name);
1237 }
1238
1239 /*
1240 * el-compatible wrapper around rl_complete; needed for key binding
1241 */
1242 /* ARGSUSED */
1243 static unsigned char
1244 _el_rl_complete(el, ch)
1245 EditLine *el;
1246 int ch;
1247 {
1248 return (unsigned char) rl_complete(0, ch);
1249 }
1250
1251 /*
1252 * returns list of completitions for text given
1253 */
1254 char **
1255 completion_matches(text, genfunc)
1256 const char *text;
1257 CPFunction *genfunc;
1258 {
1259 char **match_list = NULL, *retstr, *prevstr;
1260 size_t math_list_len, max_equal, which, i;
1261 int matches;
1262
1263 if (h == NULL || e == NULL)
1264 rl_initialize();
1265
1266 matches = 0;
1267 math_list_len = 1;
1268 while ((retstr = (*genfunc) (text, matches)) != NULL) {
1269 if (matches + 1 >= math_list_len) {
1270 math_list_len <<= 1;
1271 match_list = realloc(match_list,
1272 math_list_len * sizeof(char *));
1273 }
1274 match_list[++matches] = retstr;
1275 }
1276
1277 if (!match_list)
1278 return (char **) NULL; /* nothing found */
1279
1280 /* find least denominator and insert it to match_list[0] */
1281 which = 2;
1282 prevstr = match_list[1];
1283 max_equal = strlen(prevstr);
1284 for (; which < matches; which++) {
1285 for (i = 0; i < max_equal &&
1286 prevstr[i] == match_list[which][i]; i++)
1287 continue;
1288 max_equal = i;
1289 }
1290
1291 retstr = malloc(max_equal + 1);
1292 (void)strncpy(retstr, match_list[1], max_equal);
1293 retstr[max_equal] = '\0';
1294 match_list[0] = retstr;
1295
1296 /* add NULL as last pointer to the array */
1297 if (matches + 1 >= math_list_len)
1298 match_list = realloc(match_list,
1299 (math_list_len + 1) * sizeof(char *));
1300 match_list[matches + 1] = (char *) NULL;
1301
1302 return match_list;
1303 }
1304
1305 /*
1306 * called by rl_complete()
1307 */
1308 /* ARGSUSED */
1309 static int
1310 rl_complete_internal(what_to_do)
1311 int what_to_do;
1312 {
1313 CPFunction *complet_func;
1314 const LineInfo *li;
1315 char *temp, *temp2, **arr;
1316 size_t len;
1317
1318 if (h == NULL || e == NULL)
1319 rl_initialize();
1320
1321 complet_func = rl_completion_entry_function;
1322 if (!complet_func)
1323 complet_func = filename_completion_function;
1324
1325 li = el_line(e);
1326 /* LINTED const cast */
1327 temp = (char *) li->cursor;
1328 while (temp > li->buffer &&
1329 !strchr(rl_basic_word_break_characters, *(temp - 1)))
1330 temp--;
1331
1332 len = li->cursor - temp;
1333 temp2 = alloca(len + 1);
1334 (void)strncpy(temp2, temp, len);
1335 temp = temp2;
1336 temp[len] = '\0';
1337
1338 /* these can be used by function called in completion_matches() */
1339 /* or (*rl_attempted_completion_function)() */
1340 rl_point = li->cursor - li->buffer;
1341 rl_end = li->lastchar - li->buffer;
1342
1343 if (!rl_attempted_completion_function)
1344 arr = completion_matches(temp, complet_func);
1345 else {
1346 int end = li->cursor - li->buffer;
1347 arr = (*rl_attempted_completion_function) (temp, (int)
1348 (end - len), end);
1349 }
1350
1351 if (arr) {
1352 int i;
1353
1354 el_deletestr(e, (int)len);
1355 el_insertstr(e, arr[0]);
1356 if (strcmp(arr[0], arr[1]) == 0) {
1357 /* lcd is valid object, so add a space to mark it */
1358 /* in case of filename completition, add a space */
1359 /* only if object found is not directory */
1360 size_t alen = strlen(arr[0]);
1361 if (complet_func != filename_completion_function
1362 || (alen > 0 && (arr[0])[alen - 1] != '/'))
1363 el_insertstr(e, " ");
1364 } else
1365 /* lcd is not a valid object - further specification */
1366 /* is needed */
1367 el_beep(e);
1368
1369 /* free elements of array and the array itself */
1370 for (i = 0; arr[i]; i++)
1371 free(arr[i]);
1372 free(arr), arr = NULL;
1373
1374 return CC_REFRESH;
1375 }
1376 return CC_NORM;
1377 }
1378
1379 /*
1380 * complete word at current point
1381 */
1382 int
1383 rl_complete(ignore, invoking_key)
1384 int ignore, invoking_key;
1385 {
1386 if (h == NULL || e == NULL)
1387 rl_initialize();
1388
1389 if (rl_inhibit_completion) {
1390 rl_insert(ignore, invoking_key);
1391 return CC_REFRESH;
1392 } else
1393 return rl_complete_internal(invoking_key);
1394 }
1395
1396 /*
1397 * misc other functions
1398 */
1399
1400 /*
1401 * bind key c to readline-type function func
1402 */
1403 int
1404 rl_bind_key(c, func)
1405 int c;
1406 int func __P((int, int));
1407 {
1408 int retval = -1;
1409
1410 if (h == NULL || e == NULL)
1411 rl_initialize();
1412
1413 if (func == rl_insert) {
1414 /* XXX notice there is no range checking of ``c'' */
1415 e->el_map.key[c] = ED_INSERT;
1416 retval = 0;
1417 }
1418 return retval;
1419 }
1420
1421 /*
1422 * read one key from input - handles chars pushed back
1423 * to input stream also
1424 */
1425 int
1426 rl_read_key()
1427 {
1428 char fooarr[2 * sizeof(int)];
1429
1430 if (e == NULL || h == NULL)
1431 rl_initialize();
1432
1433 return el_getc(e, fooarr);
1434 }
1435
1436 /*
1437 * reset the terminal
1438 */
1439 /* ARGSUSED */
1440 void
1441 rl_reset_terminal(p)
1442 const char *p;
1443 {
1444 if (h == NULL || e == NULL)
1445 rl_initialize();
1446 el_reset(e);
1447 }
1448
1449 /*
1450 * insert character ``c'' back into input stream, ``count'' times
1451 */
1452 int
1453 rl_insert(count, c)
1454 int count, c;
1455 {
1456 char arr[2];
1457
1458 if (h == NULL || e == NULL)
1459 rl_initialize();
1460
1461 /* XXX - int -> char conversion can lose on multichars */
1462 arr[0] = c;
1463 arr[1] = '\0';
1464
1465 for (; count > 0; count--)
1466 el_push(e, arr);
1467
1468 return 0;
1469 }
1470