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