Annotation of src/lib/libedit/readline.c, Revision 1.1
1.1 ! christos 1: /* $NetBSD$ */
! 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$");
! 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 const 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 *, 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: */
! 119: static const HIST_ENTRY *
! 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;
! 158: e = el_init(rl_readline_name, rl_instream, rl_outstream);
! 159:
! 160: h = history_init();
! 161: if (!e || !h)
! 162: return -1;
! 163:
! 164: history(h, &ev, H_SETMAXSIZE, INT_MAX); /* unlimited */
! 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;
! 204: size_t count;
! 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) {
! 389: const HIST_ENTRY *rl_he;
! 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++;
! 408: else if (isdigit(*cmd)) {
! 409: const char *temp;
! 410: int shifted = 0;
! 411:
! 412: start = atoi(cmd);
! 413: temp = cmd;
! 414: for (; isdigit(*cmd); cmd++);
! 415: if (temp != cmd)
! 416: shifted = 1;
! 417: if (shifted && *cmd == '-') {
! 418: if (!isdigit(*(cmd + 1)))
! 419: end = -2;
! 420: else {
! 421: end = atoi(cmd + 1);
! 422: for (; isdigit(*cmd); cmd++);
! 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: }
! 569: while (len > 0 && isspace(temp[len - 1]))
! 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++;
! 634: } else if (isspace(str[j]))
! 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++) {
! 702: while (isspace(str[i]))
! 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 &&
! 711: (isspace(str[i]) || strchr("()<>;&|$", str[i])))
! 712: break;
! 713: else if (!delim && strchr("'`\"", str[i]))
! 714: delim = str[i];
! 715: }
! 716:
! 717: if (result_idx + 2 >= size) {
! 718: size <<= 1;
! 719: result = realloc(result, size * sizeof(char *));
! 720: }
! 721: len = i - start;
! 722: temp = malloc(len + 1);
! 723: (void)strncpy(temp, &str[start], len);
! 724: temp[len] = '\0';
! 725: result[result_idx++] = temp;
! 726: result[result_idx] = NULL;
! 727: }
! 728:
! 729: return result;
! 730: }
! 731:
! 732: /*
! 733: * limit size of history record to ``max'' events
! 734: */
! 735: void
! 736: stifle_history(max)
! 737: int max;
! 738: {
! 739: HistEvent ev;
! 740:
! 741: if (h == NULL || e == NULL)
! 742: rl_initialize();
! 743:
! 744: if (history(h, &ev, H_SETMAXSIZE, max) == 0)
! 745: max_input_history = max;
! 746: }
! 747:
! 748: /*
! 749: * "unlimit" size of history - set the limit to maximum allowed int value
! 750: */
! 751: int
! 752: unstifle_history()
! 753: {
! 754: HistEvent ev;
! 755: int omax;
! 756:
! 757: history(h, &ev, H_SETMAXSIZE, INT_MAX);
! 758: omax = max_input_history;
! 759: max_input_history = INT_MAX;
! 760: return omax; /* some value _must_ be returned */
! 761: }
! 762:
! 763: int
! 764: history_is_stifled()
! 765: {
! 766: /* cannot return true answer */
! 767: return (max_input_history != INT_MAX);
! 768: }
! 769:
! 770: /*
! 771: * read history from a file given
! 772: */
! 773: int
! 774: read_history(filename)
! 775: const char *filename;
! 776: {
! 777: HistEvent ev;
! 778:
! 779: if (h == NULL || e == NULL)
! 780: rl_initialize();
! 781: return history(h, &ev, H_LOAD, filename);
! 782: }
! 783:
! 784: /*
! 785: * write history to a file given
! 786: */
! 787: int
! 788: write_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_SAVE, filename);
! 796: }
! 797:
! 798: /*
! 799: * returns history ``num''th event
! 800: *
! 801: * returned pointer points to static variable
! 802: */
! 803: const HIST_ENTRY *
! 804: history_get(num)
! 805: int num;
! 806: {
! 807: static HIST_ENTRY she;
! 808: HistEvent ev;
! 809: int i = 1, curr_num;
! 810:
! 811: if (h == NULL || e == NULL)
! 812: rl_initialize();
! 813:
! 814: /* rewind to beginning */
! 815: if (history(h, &ev, H_CURR) != 0)
! 816: return NULL;
! 817: curr_num = ev.num;
! 818: if (history(h, &ev, H_LAST) != 0)
! 819: return NULL; /* error */
! 820: while (i < num && history(h, &ev, H_PREV) == 0)
! 821: i++;
! 822: if (i != num)
! 823: return NULL; /* not so many entries */
! 824:
! 825: she.line = ev.str;
! 826: she.data = NULL;
! 827:
! 828: /* rewind history to the same event it was before */
! 829: (void) history(h, &ev, H_FIRST);
! 830: (void) history(h, &ev, H_NEXT_EVENT, curr_num);
! 831:
! 832: return &she;
! 833: }
! 834:
! 835: /*
! 836: * add the line to history table
! 837: */
! 838: int
! 839: add_history(line)
! 840: const char *line;
! 841: {
! 842: HistEvent ev;
! 843:
! 844: if (h == NULL || e == NULL)
! 845: rl_initialize();
! 846:
! 847: (void) history(h, &ev, H_ENTER, line);
! 848: if (history(h, &ev, H_GETSIZE) == 0)
! 849: history_length = ev.num;
! 850:
! 851: return (!(history_length > 0)); /* return 0 if all is okay */
! 852: }
! 853:
! 854: /*
! 855: * clear the history list - delete all entries
! 856: */
! 857: void
! 858: clear_history()
! 859: {
! 860: HistEvent ev;
! 861: history(h, &ev, H_CLEAR);
! 862: }
! 863:
! 864: /*
! 865: * returns offset of the current history event
! 866: */
! 867: int
! 868: where_history()
! 869: {
! 870: HistEvent ev;
! 871: int curr_num, off;
! 872:
! 873: if (history(h, &ev, H_CURR) != 0)
! 874: return 0;
! 875: curr_num = ev.num;
! 876:
! 877: history(h, &ev, H_FIRST);
! 878: off = 1;
! 879: while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0)
! 880: off++;
! 881:
! 882: return off;
! 883: }
! 884:
! 885: /*
! 886: * returns current history event or NULL if there is no such event
! 887: */
! 888: const HIST_ENTRY *
! 889: current_history()
! 890: {
! 891: return _move_history(H_CURR);
! 892: }
! 893:
! 894: /*
! 895: * returns total number of bytes history events' data are using
! 896: */
! 897: int
! 898: history_total_bytes()
! 899: {
! 900: HistEvent ev;
! 901: int curr_num, size;
! 902:
! 903: if (history(h, &ev, H_CURR) != 0)
! 904: return -1;
! 905: curr_num = ev.num;
! 906:
! 907: history(h, &ev, H_FIRST);
! 908: size = 0;
! 909: do
! 910: size += strlen(ev.str);
! 911: while (history(h, &ev, H_NEXT) == 0);
! 912:
! 913: /* get to the same position as before */
! 914: history(h, &ev, H_PREV_EVENT, curr_num);
! 915:
! 916: return size;
! 917: }
! 918:
! 919: /*
! 920: * sets the position in the history list to ``pos''
! 921: */
! 922: int
! 923: history_set_pos(pos)
! 924: int pos;
! 925: {
! 926: HistEvent ev;
! 927: int off, curr_num;
! 928:
! 929: if (pos > history_length || pos < 0)
! 930: return -1;
! 931:
! 932: history(h, &ev, H_CURR);
! 933: curr_num = ev.num;
! 934: history(h, &ev, H_FIRST);
! 935: off = 0;
! 936: while (off < pos && history(h, &ev, H_NEXT) == 0)
! 937: off++;
! 938:
! 939: if (off != pos) { /* do a rollback in case of error */
! 940: history(h, &ev, H_FIRST);
! 941: history(h, &ev, H_NEXT_EVENT, curr_num);
! 942: return -1;
! 943: }
! 944: return 0;
! 945: }
! 946:
! 947: /*
! 948: * returns previous event in history and shifts pointer accordingly
! 949: */
! 950: const HIST_ENTRY *
! 951: previous_history()
! 952: {
! 953: return _move_history(H_PREV);
! 954: }
! 955:
! 956: /*
! 957: * returns next event in history and shifts pointer accordingly
! 958: */
! 959: const HIST_ENTRY *
! 960: next_history()
! 961: {
! 962: return _move_history(H_NEXT);
! 963: }
! 964:
! 965: /*
! 966: * generic history search function
! 967: */
! 968: static int
! 969: _history_search_gen(str, direction, pos)
! 970: const char *str;
! 971: int direction, pos;
! 972: {
! 973: HistEvent ev;
! 974: const char *strp;
! 975: int curr_num;
! 976:
! 977: if (history(h, &ev, H_CURR) != 0)
! 978: return -1;
! 979: curr_num = ev.num;
! 980:
! 981: for (;;) {
! 982: strp = strstr(ev.str, str);
! 983: if (strp && (pos < 0 || &ev.str[pos] == strp))
! 984: return (int) (strp - ev.str);
! 985: if (history(h, &ev, direction < 0 ? H_PREV : H_NEXT) != 0)
! 986: break;
! 987: }
! 988:
! 989: history(h, &ev, direction < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
! 990:
! 991: return -1;
! 992: }
! 993:
! 994: /*
! 995: * searches for first history event containing the str
! 996: */
! 997: int
! 998: history_search(str, direction)
! 999: const char *str;
! 1000: int direction;
! 1001: {
! 1002: return _history_search_gen(str, direction, -1);
! 1003: }
! 1004:
! 1005: /*
! 1006: * searches for first history event beginning with str
! 1007: */
! 1008: int
! 1009: history_search_prefix(str, direction)
! 1010: const char *str;
! 1011: int direction;
! 1012: {
! 1013: return _history_search_gen(str, direction, 0);
! 1014: }
! 1015:
! 1016: /*
! 1017: * search for event in history containing str, starting at offset
! 1018: * abs(pos); continue backward, if pos<0, forward otherwise
! 1019: */
! 1020: int
! 1021: history_search_pos(str, direction, pos)
! 1022: const char *str;
! 1023: int direction, pos;
! 1024: {
! 1025: HistEvent ev;
! 1026: int curr_num, off;
! 1027:
! 1028: off = (pos > 0) ? pos : -pos;
! 1029: pos = (pos > 0) ? 1 : -1;
! 1030:
! 1031: if (history(h, &ev, H_CURR) != 0)
! 1032: return -1;
! 1033: curr_num = ev.num;
! 1034:
! 1035: if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0)
! 1036: return -1;
! 1037:
! 1038:
! 1039: for (;;) {
! 1040: if (strstr(ev.str, str))
! 1041: return off;
! 1042: if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)
! 1043: break;
! 1044: }
! 1045:
! 1046: /* set "current" pointer back to previous state */
! 1047: history(h, &ev, (pos < 0) ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
! 1048:
! 1049: return -1;
! 1050: }
! 1051:
! 1052:
! 1053: /********************************/
! 1054: /* completition functions */
! 1055:
! 1056: /*
! 1057: * does tilde expansion of strings of type ``~user/foo''
! 1058: * if ``user'' isn't valid user name or ``txt'' doesn't start
! 1059: * w/ '~', returns pointer to strdup()ed copy of ``txt''
! 1060: *
! 1061: * it's callers's responsibility to free() returned string
! 1062: */
! 1063: char *
! 1064: tilde_expand(txt)
! 1065: const char *txt;
! 1066: {
! 1067: struct passwd *pass;
! 1068: char *temp;
! 1069: size_t len = 0;
! 1070:
! 1071: if (txt[0] != '~')
! 1072: return strdup(txt);
! 1073:
! 1074: temp = strchr(txt + 1, '/');
! 1075: if (temp == NULL)
! 1076: temp = strdup(txt + 1);
! 1077: else {
! 1078: len = temp - txt + 1; /* text until string after slash */
! 1079: temp = malloc(len);
! 1080: (void)strncpy(temp, txt + 1, len - 2);
! 1081: temp[len - 2] = '\0';
! 1082: }
! 1083: pass = getpwnam(temp);
! 1084: free(temp); /* value no more needed */
! 1085: if (pass == NULL)
! 1086: return strdup(txt);
! 1087:
! 1088: /* update pointer txt to point at string immedially following */
! 1089: /* first slash */
! 1090: txt += len;
! 1091:
! 1092: temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1);
! 1093: (void)sprintf(temp, "%s/%s", pass->pw_dir, txt);
! 1094:
! 1095: return temp;
! 1096: }
! 1097:
! 1098: /*
! 1099: * return first found file name starting by the ``text'' or NULL if no
! 1100: * such file can be found
! 1101: * value of ``state'' is ignored
! 1102: *
! 1103: * it's caller's responsibility to free returned string
! 1104: */
! 1105: char *
! 1106: filename_completion_function(text, state)
! 1107: const char *text;
! 1108: int state;
! 1109: {
! 1110: static DIR *dir = NULL;
! 1111: static char *filename = NULL, *dirname = NULL;
! 1112: static size_t filename_len = 0;
! 1113: struct dirent *entry;
! 1114: char *temp;
! 1115: size_t len;
! 1116:
! 1117: if (state == 0 || dir == NULL) {
! 1118: if (dir != NULL) {
! 1119: closedir(dir);
! 1120: dir = NULL;
! 1121: }
! 1122: temp = strrchr(text, '/');
! 1123: if (temp) {
! 1124: temp++;
! 1125: filename = realloc(filename, strlen(temp) + 1);
! 1126: (void)strcpy(filename, temp);
! 1127: len = temp - text; /* including last slash */
! 1128: dirname = realloc(dirname, len + 1);
! 1129: (void)strncpy(dirname, text, len);
! 1130: dirname[len] = '\0';
! 1131: } else {
! 1132: filename = strdup(text);
! 1133: dirname = NULL;
! 1134: }
! 1135:
! 1136: /* support for ``~user'' syntax */
! 1137: if (dirname && *dirname == '~') {
! 1138: temp = tilde_expand(dirname);
! 1139: dirname = realloc(dirname, strlen(temp) + 1);
! 1140: (void)strcpy(dirname, temp); /* safe */
! 1141: free(temp); /* no more needed */
! 1142: }
! 1143: /* will be used in cycle */
! 1144: filename_len = strlen(filename);
! 1145: if (filename_len == 0)
! 1146: return NULL; /* no expansion possible */
! 1147:
! 1148: dir = opendir(dirname ? dirname : ".");
! 1149: if (!dir)
! 1150: return NULL; /* cannot open the directory */
! 1151: }
! 1152: /* find the match */
! 1153: while ((entry = readdir(dir))) {
! 1154: /* otherwise, get first entry where first */
! 1155: /* filename_len characters are equal */
! 1156: if (entry->d_name[0] == filename[0]
! 1157: && entry->d_namlen >= filename_len
! 1158: && strncmp(entry->d_name, filename,
! 1159: filename_len) == 0)
! 1160: break;
! 1161: }
! 1162:
! 1163: if (entry) { /* match found */
! 1164:
! 1165: struct stat stbuf;
! 1166: len = entry->d_namlen +
! 1167: ((dirname) ? strlen(dirname) : 0) + 1 + 1;
! 1168: temp = malloc(len);
! 1169: (void)sprintf(temp, "%s%s",
! 1170: dirname ? dirname : "", entry->d_name); /* safe */
! 1171:
! 1172: /* test, if it's directory */
! 1173: if (stat(temp, &stbuf) == 0 && S_ISDIR(stbuf.st_mode))
! 1174: strcat(temp, "/"); /* safe */
! 1175: } else
! 1176: temp = NULL;
! 1177:
! 1178: return temp;
! 1179: }
! 1180:
! 1181: /*
! 1182: * a completion generator for usernames; returns _first_ username
! 1183: * which starts with supplied text
! 1184: * text contains a partial username preceded by random character
! 1185: * (usually '~'); state is ignored
! 1186: * it's callers responsibility to free returned value
! 1187: */
! 1188: char *
! 1189: username_completion_function(text, state)
! 1190: const char *text;
! 1191: int state;
! 1192: {
! 1193: struct passwd *pwd;
! 1194:
! 1195: if (text[0] == '\0')
! 1196: return NULL;
! 1197:
! 1198: if (*text == '~')
! 1199: text++;
! 1200:
! 1201: if (state == 0)
! 1202: setpwent();
! 1203:
! 1204: while ((pwd = getpwent()) && text[0] == pwd->pw_name[0]
! 1205: && strcmp(text, pwd->pw_name) == 0);
! 1206:
! 1207: if (pwd == NULL) {
! 1208: endpwent();
! 1209: return NULL;
! 1210: }
! 1211: return strdup(pwd->pw_name);
! 1212: }
! 1213:
! 1214: /*
! 1215: * el-compatible wrapper around rl_complete; needed for key binding
! 1216: */
! 1217: static unsigned char
! 1218: _el_rl_complete(el, ch)
! 1219: EditLine *el;
! 1220: int ch;
! 1221: {
! 1222: return (unsigned char) rl_complete(0, ch);
! 1223: }
! 1224:
! 1225: /*
! 1226: * returns list of completitions for text given
! 1227: */
! 1228: char **
! 1229: completion_matches(text, genfunc)
! 1230: const char *text;
! 1231: CPFunction *genfunc;
! 1232: {
! 1233: char **match_list = NULL, *retstr, *prevstr;
! 1234: size_t matches, math_list_len, max_equal, len, which,
! 1235: i;
! 1236:
! 1237: if (h == NULL || e == NULL)
! 1238: rl_initialize();
! 1239:
! 1240: matches = 0;
! 1241: math_list_len = 1;
! 1242: while ((retstr = (*genfunc) (text, matches))) {
! 1243: if (matches + 1 >= math_list_len) {
! 1244: math_list_len <<= 1;
! 1245: match_list = realloc(match_list,
! 1246: math_list_len * sizeof(char *));
! 1247: }
! 1248: match_list[++matches] = retstr;
! 1249: }
! 1250:
! 1251: if (!match_list)
! 1252: return (char **) NULL; /* nothing found */
! 1253:
! 1254: /* find least denominator and insert it to match_list[0] */
! 1255: which = 2;
! 1256: prevstr = match_list[1];
! 1257: len = max_equal = strlen(prevstr);
! 1258: for (; which < matches; which++) {
! 1259: for (i = 0; i < max_equal &&
! 1260: prevstr[i] == match_list[which][i]; i++)
! 1261: continue;
! 1262: max_equal = i;
! 1263: }
! 1264:
! 1265: retstr = malloc(max_equal + 1);
! 1266: (void)strncpy(retstr, match_list[1], max_equal);
! 1267: retstr[max_equal] = '\0';
! 1268: match_list[0] = retstr;
! 1269:
! 1270: /* add NULL as last pointer to the array */
! 1271: if (matches + 1 >= math_list_len)
! 1272: match_list = realloc(match_list,
! 1273: (math_list_len + 1) * sizeof(char *));
! 1274: match_list[matches + 1] = (char *) NULL;
! 1275:
! 1276: return match_list;
! 1277: }
! 1278:
! 1279: /*
! 1280: * called by rl_complete()
! 1281: */
! 1282: static int
! 1283: rl_complete_internal(what_to_do)
! 1284: int what_to_do;
! 1285: {
! 1286: CPFunction *complet_func;
! 1287: const LineInfo *li;
! 1288: char *temp, *temp2, **arr;
! 1289: int len;
! 1290:
! 1291: if (h == NULL || e == NULL)
! 1292: rl_initialize();
! 1293:
! 1294: complet_func = rl_completion_entry_function;
! 1295: if (!complet_func)
! 1296: complet_func = filename_completion_function;
! 1297:
! 1298: li = el_line(e);
! 1299: temp = (char *) li->cursor;
! 1300: while (temp > li->buffer &&
! 1301: !strchr(rl_basic_word_break_characters, *(temp - 1)))
! 1302: temp--;
! 1303:
! 1304: len = li->cursor - temp;
! 1305: temp2 = alloca(len + 1);
! 1306: (void)strncpy(temp2, temp, len);
! 1307: temp = temp2;
! 1308: temp[len] = '\0';
! 1309:
! 1310: /* these can be used by function called in completion_matches() */
! 1311: /* or (*rl_attempted_completion_function)() */
! 1312: rl_point = li->cursor - li->buffer;
! 1313: rl_end = li->lastchar - li->buffer;
! 1314:
! 1315: if (!rl_attempted_completion_function)
! 1316: arr = completion_matches(temp, complet_func);
! 1317: else {
! 1318: int end = li->cursor - li->buffer;
! 1319: arr = (*rl_attempted_completion_function) (temp,
! 1320: end - len, end);
! 1321: }
! 1322: free(temp); /* no more needed */
! 1323:
! 1324: if (arr) {
! 1325: int i;
! 1326:
! 1327: el_deletestr(e, len);
! 1328: el_insertstr(e, arr[0]);
! 1329: if (strcmp(arr[0], arr[1]) == 0) {
! 1330: /* lcd is valid object, so add a space to mark it */
! 1331: /* in case of filename completition, add a space */
! 1332: /* only if object found is not directory */
! 1333: int len = strlen(arr[0]);
! 1334: if (complet_func != filename_completion_function
! 1335: || (len > 0 && (arr[0])[len - 1] != '/'))
! 1336: el_insertstr(e, " ");
! 1337: } else
! 1338: /* lcd is not a valid object - further specification */
! 1339: /* is needed */
! 1340: term_beep(e);
! 1341:
! 1342: /* free elements of array and the array itself */
! 1343: for (i = 0; arr[i]; i++)
! 1344: free(arr[i]);
! 1345: free(arr), arr = NULL;
! 1346:
! 1347: return CC_REFRESH;
! 1348: }
! 1349: return CC_NORM;
! 1350: }
! 1351:
! 1352: /*
! 1353: * complete word at current point
! 1354: */
! 1355: int
! 1356: rl_complete(ignore, invoking_key)
! 1357: int ignore, invoking_key;
! 1358: {
! 1359: if (h == NULL || e == NULL)
! 1360: rl_initialize();
! 1361:
! 1362: if (rl_inhibit_completion) {
! 1363: rl_insert(ignore, invoking_key);
! 1364: return CC_REFRESH;
! 1365: } else
! 1366: return rl_complete_internal(invoking_key);
! 1367:
! 1368: return CC_REFRESH;
! 1369: }
! 1370:
! 1371: /*
! 1372: * misc other functions
! 1373: */
! 1374:
! 1375: /*
! 1376: * bind key c to readline-type function func
! 1377: */
! 1378: int
! 1379: rl_bind_key(c, func)
! 1380: int c;
! 1381: int func __P((int, int));
! 1382: {
! 1383: int retval = -1;
! 1384:
! 1385: if (h == NULL || e == NULL)
! 1386: rl_initialize();
! 1387:
! 1388: if (func == rl_insert) {
! 1389: /* XXX notice there is no range checking of ``c'' */
! 1390: e->el_map.key[c] = ED_INSERT;
! 1391: retval = 0;
! 1392: }
! 1393: return retval;
! 1394: }
! 1395:
! 1396: /*
! 1397: * read one key from input - handles chars pushed back
! 1398: * to input stream also
! 1399: */
! 1400: int
! 1401: rl_read_key()
! 1402: {
! 1403: char fooarr[2 * sizeof(int)];
! 1404:
! 1405: if (e == NULL || h == NULL)
! 1406: rl_initialize();
! 1407:
! 1408: return el_getc(e, fooarr);
! 1409: }
! 1410:
! 1411: /*
! 1412: * reset the terminal
! 1413: */
! 1414: void
! 1415: rl_reset_terminal(p)
! 1416: const char *p;
! 1417: {
! 1418: if (h == NULL || e == NULL)
! 1419: rl_initialize();
! 1420: el_reset(e);
! 1421: }
! 1422:
! 1423: /*
! 1424: * insert character ``c'' back into input stream, ``count'' times
! 1425: */
! 1426: int
! 1427: rl_insert(count, c)
! 1428: int count, c;
! 1429: {
! 1430: char arr[2];
! 1431:
! 1432: if (h == NULL || e == NULL)
! 1433: rl_initialize();
! 1434:
! 1435: /* XXX - int -> char conversion can lose on multichars */
! 1436: arr[0] = c;
! 1437: arr[1] = '\0';
! 1438:
! 1439: for (; count > 0; count--)
! 1440: el_push(e, arr);
! 1441:
! 1442: return 0;
! 1443: }
CVSweb <webmaster@jp.NetBSD.org>