histexpand.c revision 58310
121308Sache/* histexpand.c -- history expansion. */ 221308Sache 321308Sache/* Copyright (C) 1989, 1992 Free Software Foundation, Inc. 421308Sache 521308Sache This file contains the GNU History Library (the Library), a set of 621308Sache routines for managing the text of previously typed lines. 721308Sache 821308Sache The Library is free software; you can redistribute it and/or modify 921308Sache it under the terms of the GNU General Public License as published by 1058310Sache the Free Software Foundation; either version 2, or (at your option) 1121308Sache any later version. 1221308Sache 1321308Sache The Library is distributed in the hope that it will be useful, but 1421308Sache WITHOUT ANY WARRANTY; without even the implied warranty of 1521308Sache MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1621308Sache General Public License for more details. 1721308Sache 1821308Sache The GNU General Public License is often shipped with GNU software, and 1921308Sache is generally kept in a file called COPYING or LICENSE. If you do not 2021308Sache have a copy of the license, write to the Free Software Foundation, 2158310Sache 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ 2221308Sache 2321308Sache#define READLINE_LIBRARY 2421308Sache 2521308Sache#if defined (HAVE_CONFIG_H) 2621308Sache# include <config.h> 2721308Sache#endif 2821308Sache 2921308Sache#include <stdio.h> 3021308Sache 3121308Sache#if defined (HAVE_STDLIB_H) 3221308Sache# include <stdlib.h> 3321308Sache#else 3421308Sache# include "ansi_stdlib.h" 3521308Sache#endif /* HAVE_STDLIB_H */ 3621308Sache 3721308Sache#if defined (HAVE_UNISTD_H) 3835486Sache# ifndef _MINIX 3935486Sache# include <sys/types.h> 4035486Sache# endif 4121308Sache# include <unistd.h> 4221308Sache#endif 4321308Sache 4421308Sache#if defined (HAVE_STRING_H) 4521308Sache# include <string.h> 4621308Sache#else 4721308Sache# include <strings.h> 4821308Sache#endif /* !HAVE_STRING_H */ 4921308Sache 5021308Sache#include "history.h" 5121308Sache#include "histlib.h" 5221308Sache 5358310Sache#include "rlshell.h" 5458310Sache#include "xmalloc.h" 5558310Sache 5635486Sache#define HISTORY_WORD_DELIMITERS " \t\n;&()|<>" 5735486Sache#define HISTORY_QUOTE_CHARACTERS "\"'`" 5835486Sache 5921308Sachestatic char error_pointer; 6021308Sache 6121308Sachestatic char *subst_lhs; 6221308Sachestatic char *subst_rhs; 6321308Sachestatic int subst_lhs_len; 6421308Sachestatic int subst_rhs_len; 6521308Sache 6658310Sachestatic char *get_history_word_specifier __P((char *, char *, int *)); 6758310Sachestatic char *history_find_word __P((char *, int)); 6821308Sache 6958310Sachestatic char *quote_breaks __P((char *)); 7021308Sache 7121308Sache/* Variables exported by this file. */ 7221308Sache/* The character that represents the start of a history expansion 7321308Sache request. This is usually `!'. */ 7421308Sachechar history_expansion_char = '!'; 7521308Sache 7621308Sache/* The character that invokes word substitution if found at the start of 7721308Sache a line. This is usually `^'. */ 7821308Sachechar history_subst_char = '^'; 7921308Sache 8021308Sache/* During tokenization, if this character is seen as the first character 8121308Sache of a word, then it, and all subsequent characters upto a newline are 8221308Sache ignored. For a Bourne shell, this should be '#'. Bash special cases 8321308Sache the interactive comment character to not be a comment delimiter. */ 8421308Sachechar history_comment_char = '\0'; 8521308Sache 8621308Sache/* The list of characters which inhibit the expansion of text if found 8721308Sache immediately following history_expansion_char. */ 8821308Sachechar *history_no_expand_chars = " \t\n\r="; 8921308Sache 9021308Sache/* If set to a non-zero value, single quotes inhibit history expansion. 9121308Sache The default is 0. */ 9221308Sacheint history_quotes_inhibit_expansion = 0; 9321308Sache 9426497Sache/* If set, this points to a function that is called to verify that a 9526497Sache particular history expansion should be performed. */ 9626497SacheFunction *history_inhibit_expansion_function; 9726497Sache 9821308Sache/* **************************************************************** */ 9921308Sache/* */ 10021308Sache/* History Expansion */ 10121308Sache/* */ 10221308Sache/* **************************************************************** */ 10321308Sache 10421308Sache/* Hairy history expansion on text, not tokens. This is of general 10521308Sache use, and thus belongs in this library. */ 10621308Sache 10721308Sache/* The last string searched for by a !?string? search. */ 10821308Sachestatic char *search_string; 10921308Sache 11021308Sache/* The last string matched by a !?string? search. */ 11121308Sachestatic char *search_match; 11221308Sache 11321308Sache/* Return the event specified at TEXT + OFFSET modifying OFFSET to 11421308Sache point to after the event specifier. Just a pointer to the history 11521308Sache line is returned; NULL is returned in the event of a bad specifier. 11621308Sache You pass STRING with *INDEX equal to the history_expansion_char that 11721308Sache begins this specification. 11821308Sache DELIMITING_QUOTE is a character that is allowed to end the string 11921308Sache specification for what to search for in addition to the normal 12021308Sache characters `:', ` ', `\t', `\n', and sometimes `?'. 12121308Sache So you might call this function like: 12221308Sache line = get_history_event ("!echo:p", &index, 0); */ 12321308Sachechar * 12421308Sacheget_history_event (string, caller_index, delimiting_quote) 12521308Sache char *string; 12621308Sache int *caller_index; 12721308Sache int delimiting_quote; 12821308Sache{ 12921308Sache register int i; 13021308Sache register char c; 13121308Sache HIST_ENTRY *entry; 13221308Sache int which, sign, local_index, substring_okay; 13321308Sache Function *search_func; 13421308Sache char *temp; 13521308Sache 13621308Sache /* The event can be specified in a number of ways. 13721308Sache 13821308Sache !! the previous command 13921308Sache !n command line N 14021308Sache !-n current command-line minus N 14121308Sache !str the most recent command starting with STR 14221308Sache !?str[?] 14321308Sache the most recent command containing STR 14421308Sache 14521308Sache All values N are determined via HISTORY_BASE. */ 14621308Sache 14721308Sache i = *caller_index; 14821308Sache 14921308Sache if (string[i] != history_expansion_char) 15021308Sache return ((char *)NULL); 15121308Sache 15221308Sache /* Move on to the specification. */ 15321308Sache i++; 15421308Sache 15521308Sache sign = 1; 15621308Sache substring_okay = 0; 15721308Sache 15821308Sache#define RETURN_ENTRY(e, w) \ 15921308Sache return ((e = history_get (w)) ? e->line : (char *)NULL) 16021308Sache 16121308Sache /* Handle !! case. */ 16221308Sache if (string[i] == history_expansion_char) 16321308Sache { 16421308Sache i++; 16521308Sache which = history_base + (history_length - 1); 16621308Sache *caller_index = i; 16721308Sache RETURN_ENTRY (entry, which); 16821308Sache } 16921308Sache 17021308Sache /* Hack case of numeric line specification. */ 17121308Sache if (string[i] == '-') 17221308Sache { 17321308Sache sign = -1; 17421308Sache i++; 17521308Sache } 17621308Sache 17721308Sache if (_rl_digit_p (string[i])) 17821308Sache { 17921308Sache /* Get the extent of the digits and compute the value. */ 18021308Sache for (which = 0; _rl_digit_p (string[i]); i++) 18121308Sache which = (which * 10) + _rl_digit_value (string[i]); 18221308Sache 18321308Sache *caller_index = i; 18421308Sache 18521308Sache if (sign < 0) 18621308Sache which = (history_length + history_base) - which; 18721308Sache 18821308Sache RETURN_ENTRY (entry, which); 18921308Sache } 19021308Sache 19121308Sache /* This must be something to search for. If the spec begins with 19221308Sache a '?', then the string may be anywhere on the line. Otherwise, 19321308Sache the string must be found at the start of a line. */ 19421308Sache if (string[i] == '?') 19521308Sache { 19621308Sache substring_okay++; 19721308Sache i++; 19821308Sache } 19921308Sache 20021308Sache /* Only a closing `?' or a newline delimit a substring search string. */ 20121308Sache for (local_index = i; c = string[i]; i++) 20221308Sache if ((!substring_okay && (whitespace (c) || c == ':' || 20321308Sache (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) || 20421308Sache string[i] == delimiting_quote)) || 20521308Sache string[i] == '\n' || 20621308Sache (substring_okay && string[i] == '?')) 20721308Sache break; 20821308Sache 20921308Sache which = i - local_index; 21021308Sache temp = xmalloc (1 + which); 21121308Sache if (which) 21221308Sache strncpy (temp, string + local_index, which); 21321308Sache temp[which] = '\0'; 21421308Sache 21521308Sache if (substring_okay && string[i] == '?') 21621308Sache i++; 21721308Sache 21821308Sache *caller_index = i; 21921308Sache 22021308Sache#define FAIL_SEARCH() \ 22121308Sache do { \ 22221308Sache history_offset = history_length; free (temp) ; return (char *)NULL; \ 22321308Sache } while (0) 22421308Sache 22521308Sache /* If there is no search string, try to use the previous search string, 22621308Sache if one exists. If not, fail immediately. */ 22721308Sache if (*temp == '\0' && substring_okay) 22821308Sache { 22921308Sache if (search_string) 23021308Sache { 23121308Sache free (temp); 23221308Sache temp = savestring (search_string); 23321308Sache } 23421308Sache else 23521308Sache FAIL_SEARCH (); 23621308Sache } 23721308Sache 23821308Sache search_func = substring_okay ? history_search : history_search_prefix; 23921308Sache while (1) 24021308Sache { 24121308Sache local_index = (*search_func) (temp, -1); 24221308Sache 24321308Sache if (local_index < 0) 24421308Sache FAIL_SEARCH (); 24521308Sache 24621308Sache if (local_index == 0 || substring_okay) 24721308Sache { 24821308Sache entry = current_history (); 24921308Sache history_offset = history_length; 25021308Sache 25121308Sache /* If this was a substring search, then remember the 25221308Sache string that we matched for word substitution. */ 25321308Sache if (substring_okay) 25421308Sache { 25521308Sache FREE (search_string); 25621308Sache search_string = temp; 25721308Sache 25821308Sache FREE (search_match); 25921308Sache search_match = history_find_word (entry->line, local_index); 26021308Sache } 26121308Sache else 26221308Sache free (temp); 26321308Sache 26421308Sache return (entry->line); 26521308Sache } 26621308Sache 26721308Sache if (history_offset) 26821308Sache history_offset--; 26921308Sache else 27021308Sache FAIL_SEARCH (); 27121308Sache } 27221308Sache#undef FAIL_SEARCH 27321308Sache#undef RETURN_ENTRY 27421308Sache} 27521308Sache 27621308Sache/* Function for extracting single-quoted strings. Used for inhibiting 27721308Sache history expansion within single quotes. */ 27821308Sache 27921308Sache/* Extract the contents of STRING as if it is enclosed in single quotes. 28021308Sache SINDEX, when passed in, is the offset of the character immediately 28121308Sache following the opening single quote; on exit, SINDEX is left pointing 28221308Sache to the closing single quote. */ 28321308Sachestatic void 28421308Sachehist_string_extract_single_quoted (string, sindex) 28521308Sache char *string; 28621308Sache int *sindex; 28721308Sache{ 28821308Sache register int i; 28921308Sache 29021308Sache for (i = *sindex; string[i] && string[i] != '\''; i++) 29121308Sache ; 29221308Sache 29321308Sache *sindex = i; 29421308Sache} 29521308Sache 29621308Sachestatic char * 29721308Sachequote_breaks (s) 29821308Sache char *s; 29921308Sache{ 30021308Sache register char *p, *r; 30121308Sache char *ret; 30221308Sache int len = 3; 30321308Sache 30421308Sache for (p = s; p && *p; p++, len++) 30521308Sache { 30621308Sache if (*p == '\'') 30721308Sache len += 3; 30821308Sache else if (whitespace (*p) || *p == '\n') 30921308Sache len += 2; 31021308Sache } 31121308Sache 31221308Sache r = ret = xmalloc (len); 31321308Sache *r++ = '\''; 31421308Sache for (p = s; p && *p; ) 31521308Sache { 31621308Sache if (*p == '\'') 31721308Sache { 31821308Sache *r++ = '\''; 31921308Sache *r++ = '\\'; 32021308Sache *r++ = '\''; 32121308Sache *r++ = '\''; 32221308Sache p++; 32321308Sache } 32421308Sache else if (whitespace (*p) || *p == '\n') 32521308Sache { 32621308Sache *r++ = '\''; 32721308Sache *r++ = *p++; 32821308Sache *r++ = '\''; 32921308Sache } 33021308Sache else 33121308Sache *r++ = *p++; 33221308Sache } 33321308Sache *r++ = '\''; 33421308Sache *r = '\0'; 33521308Sache return ret; 33621308Sache} 33721308Sache 33821308Sachestatic char * 33921308Sachehist_error(s, start, current, errtype) 34021308Sache char *s; 34121308Sache int start, current, errtype; 34221308Sache{ 34321308Sache char *temp, *emsg; 34421308Sache int ll, elen; 34521308Sache 34621308Sache ll = current - start; 34721308Sache 34821308Sache switch (errtype) 34921308Sache { 35021308Sache case EVENT_NOT_FOUND: 35121308Sache emsg = "event not found"; 35221308Sache elen = 15; 35321308Sache break; 35421308Sache case BAD_WORD_SPEC: 35521308Sache emsg = "bad word specifier"; 35621308Sache elen = 18; 35721308Sache break; 35821308Sache case SUBST_FAILED: 35921308Sache emsg = "substitution failed"; 36021308Sache elen = 19; 36121308Sache break; 36221308Sache case BAD_MODIFIER: 36321308Sache emsg = "unrecognized history modifier"; 36421308Sache elen = 29; 36521308Sache break; 36647558Sache case NO_PREV_SUBST: 36747558Sache emsg = "no previous substitution"; 36847558Sache elen = 24; 36947558Sache break; 37021308Sache default: 37121308Sache emsg = "unknown expansion error"; 37221308Sache elen = 23; 37321308Sache break; 37421308Sache } 37521308Sache 37621308Sache temp = xmalloc (ll + elen + 3); 37721308Sache strncpy (temp, s + start, ll); 37821308Sache temp[ll] = ':'; 37921308Sache temp[ll + 1] = ' '; 38021308Sache strcpy (temp + ll + 2, emsg); 38121308Sache return (temp); 38221308Sache} 38321308Sache 38421308Sache/* Get a history substitution string from STR starting at *IPTR 38521308Sache and return it. The length is returned in LENPTR. 38621308Sache 38721308Sache A backslash can quote the delimiter. If the string is the 38821308Sache empty string, the previous pattern is used. If there is 38921308Sache no previous pattern for the lhs, the last history search 39021308Sache string is used. 39121308Sache 39221308Sache If IS_RHS is 1, we ignore empty strings and set the pattern 39321308Sache to "" anyway. subst_lhs is not changed if the lhs is empty; 39421308Sache subst_rhs is allowed to be set to the empty string. */ 39521308Sache 39621308Sachestatic char * 39721308Sacheget_subst_pattern (str, iptr, delimiter, is_rhs, lenptr) 39821308Sache char *str; 39921308Sache int *iptr, delimiter, is_rhs, *lenptr; 40021308Sache{ 40121308Sache register int si, i, j, k; 40221308Sache char *s = (char *) NULL; 40321308Sache 40421308Sache i = *iptr; 40521308Sache 40621308Sache for (si = i; str[si] && str[si] != delimiter; si++) 40721308Sache if (str[si] == '\\' && str[si + 1] == delimiter) 40821308Sache si++; 40921308Sache 41021308Sache if (si > i || is_rhs) 41121308Sache { 41221308Sache s = xmalloc (si - i + 1); 41321308Sache for (j = 0, k = i; k < si; j++, k++) 41421308Sache { 41521308Sache /* Remove a backslash quoting the search string delimiter. */ 41621308Sache if (str[k] == '\\' && str[k + 1] == delimiter) 41721308Sache k++; 41821308Sache s[j] = str[k]; 41921308Sache } 42021308Sache s[j] = '\0'; 42121308Sache if (lenptr) 42221308Sache *lenptr = j; 42321308Sache } 42421308Sache 42521308Sache i = si; 42621308Sache if (str[i]) 42721308Sache i++; 42821308Sache *iptr = i; 42921308Sache 43021308Sache return s; 43121308Sache} 43221308Sache 43321308Sachestatic void 43421308Sachepostproc_subst_rhs () 43521308Sache{ 43621308Sache char *new; 43721308Sache int i, j, new_size; 43821308Sache 43921308Sache new = xmalloc (new_size = subst_rhs_len + subst_lhs_len); 44021308Sache for (i = j = 0; i < subst_rhs_len; i++) 44121308Sache { 44221308Sache if (subst_rhs[i] == '&') 44321308Sache { 44421308Sache if (j + subst_lhs_len >= new_size) 44521308Sache new = xrealloc (new, (new_size = new_size * 2 + subst_lhs_len)); 44621308Sache strcpy (new + j, subst_lhs); 44721308Sache j += subst_lhs_len; 44821308Sache } 44921308Sache else 45021308Sache { 45121308Sache /* a single backslash protects the `&' from lhs interpolation */ 45221308Sache if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&') 45321308Sache i++; 45421308Sache if (j >= new_size) 45521308Sache new = xrealloc (new, new_size *= 2); 45621308Sache new[j++] = subst_rhs[i]; 45721308Sache } 45821308Sache } 45921308Sache new[j] = '\0'; 46021308Sache free (subst_rhs); 46121308Sache subst_rhs = new; 46221308Sache subst_rhs_len = j; 46321308Sache} 46421308Sache 46521308Sache/* Expand the bulk of a history specifier starting at STRING[START]. 46621308Sache Returns 0 if everything is OK, -1 if an error occurred, and 1 46721308Sache if the `p' modifier was supplied and the caller should just print 46821308Sache the returned string. Returns the new index into string in 46921308Sache *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */ 47021308Sachestatic int 47121308Sachehistory_expand_internal (string, start, end_index_ptr, ret_string, current_line) 47221308Sache char *string; 47321308Sache int start, *end_index_ptr; 47421308Sache char **ret_string; 47521308Sache char *current_line; /* for !# */ 47621308Sache{ 47721308Sache int i, n, starting_index; 47821308Sache int substitute_globally, want_quotes, print_only; 47921308Sache char *event, *temp, *result, *tstr, *t, c, *word_spec; 48021308Sache int result_len; 48121308Sache 48221308Sache result = xmalloc (result_len = 128); 48321308Sache 48421308Sache i = start; 48521308Sache 48621308Sache /* If it is followed by something that starts a word specifier, 48721308Sache then !! is implied as the event specifier. */ 48821308Sache 48921308Sache if (member (string[i + 1], ":$*%^")) 49021308Sache { 49121308Sache char fake_s[3]; 49221308Sache int fake_i = 0; 49321308Sache i++; 49421308Sache fake_s[0] = fake_s[1] = history_expansion_char; 49521308Sache fake_s[2] = '\0'; 49621308Sache event = get_history_event (fake_s, &fake_i, 0); 49721308Sache } 49821308Sache else if (string[i + 1] == '#') 49921308Sache { 50021308Sache i += 2; 50121308Sache event = current_line; 50221308Sache } 50321308Sache else 50421308Sache { 50521308Sache int quoted_search_delimiter = 0; 50621308Sache 50721308Sache /* If the character before this `!' is a double or single 50821308Sache quote, then this expansion takes place inside of the 50921308Sache quoted string. If we have to search for some text ("!foo"), 51021308Sache allow the delimiter to end the search string. */ 51121308Sache if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) 51221308Sache quoted_search_delimiter = string[i - 1]; 51321308Sache event = get_history_event (string, &i, quoted_search_delimiter); 51421308Sache } 51521308Sache 51621308Sache if (event == 0) 51721308Sache { 51821308Sache *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND); 51921308Sache free (result); 52021308Sache return (-1); 52121308Sache } 52221308Sache 52321308Sache /* If a word specifier is found, then do what that requires. */ 52421308Sache starting_index = i; 52521308Sache word_spec = get_history_word_specifier (string, event, &i); 52621308Sache 52721308Sache /* There is no such thing as a `malformed word specifier'. However, 52821308Sache it is possible for a specifier that has no match. In that case, 52921308Sache we complain. */ 53021308Sache if (word_spec == (char *)&error_pointer) 53121308Sache { 53221308Sache *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC); 53321308Sache free (result); 53421308Sache return (-1); 53521308Sache } 53621308Sache 53721308Sache /* If no word specifier, than the thing of interest was the event. */ 53821308Sache temp = word_spec ? savestring (word_spec) : savestring (event); 53921308Sache FREE (word_spec); 54021308Sache 54121308Sache /* Perhaps there are other modifiers involved. Do what they say. */ 54221308Sache want_quotes = substitute_globally = print_only = 0; 54321308Sache starting_index = i; 54421308Sache 54521308Sache while (string[i] == ':') 54621308Sache { 54721308Sache c = string[i + 1]; 54821308Sache 54921308Sache if (c == 'g') 55021308Sache { 55121308Sache substitute_globally = 1; 55221308Sache i++; 55321308Sache c = string[i + 1]; 55421308Sache } 55521308Sache 55621308Sache switch (c) 55721308Sache { 55821308Sache default: 55921308Sache *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER); 56021308Sache free (result); 56121308Sache free (temp); 56221308Sache return -1; 56321308Sache 56421308Sache case 'q': 56521308Sache want_quotes = 'q'; 56621308Sache break; 56721308Sache 56821308Sache case 'x': 56921308Sache want_quotes = 'x'; 57021308Sache break; 57121308Sache 57221308Sache /* :p means make this the last executed line. So we 57321308Sache return an error state after adding this line to the 57421308Sache history. */ 57521308Sache case 'p': 57621308Sache print_only++; 57721308Sache break; 57821308Sache 57921308Sache /* :t discards all but the last part of the pathname. */ 58021308Sache case 't': 58121308Sache tstr = strrchr (temp, '/'); 58221308Sache if (tstr) 58321308Sache { 58421308Sache tstr++; 58521308Sache t = savestring (tstr); 58621308Sache free (temp); 58721308Sache temp = t; 58821308Sache } 58921308Sache break; 59021308Sache 59121308Sache /* :h discards the last part of a pathname. */ 59221308Sache case 'h': 59321308Sache tstr = strrchr (temp, '/'); 59421308Sache if (tstr) 59521308Sache *tstr = '\0'; 59621308Sache break; 59721308Sache 59821308Sache /* :r discards the suffix. */ 59921308Sache case 'r': 60021308Sache tstr = strrchr (temp, '.'); 60121308Sache if (tstr) 60221308Sache *tstr = '\0'; 60321308Sache break; 60421308Sache 60521308Sache /* :e discards everything but the suffix. */ 60621308Sache case 'e': 60721308Sache tstr = strrchr (temp, '.'); 60821308Sache if (tstr) 60921308Sache { 61021308Sache t = savestring (tstr); 61121308Sache free (temp); 61221308Sache temp = t; 61321308Sache } 61421308Sache break; 61521308Sache 61621308Sache /* :s/this/that substitutes `that' for the first 61721308Sache occurrence of `this'. :gs/this/that substitutes `that' 61821308Sache for each occurrence of `this'. :& repeats the last 61921308Sache substitution. :g& repeats the last substitution 62021308Sache globally. */ 62121308Sache 62221308Sache case '&': 62321308Sache case 's': 62421308Sache { 62521308Sache char *new_event, *t; 62621308Sache int delimiter, failed, si, l_temp; 62721308Sache 62821308Sache if (c == 's') 62921308Sache { 63021308Sache if (i + 2 < (int)strlen (string)) 63121308Sache delimiter = string[i + 2]; 63221308Sache else 63321308Sache break; /* no search delimiter */ 63421308Sache 63521308Sache i += 3; 63621308Sache 63721308Sache t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len); 63821308Sache /* An empty substitution lhs with no previous substitution 63921308Sache uses the last search string as the lhs. */ 64021308Sache if (t) 64121308Sache { 64221308Sache FREE (subst_lhs); 64321308Sache subst_lhs = t; 64421308Sache } 64521308Sache else if (!subst_lhs) 64621308Sache { 64721308Sache if (search_string && *search_string) 64821308Sache { 64921308Sache subst_lhs = savestring (search_string); 65021308Sache subst_lhs_len = strlen (subst_lhs); 65121308Sache } 65221308Sache else 65321308Sache { 65421308Sache subst_lhs = (char *) NULL; 65521308Sache subst_lhs_len = 0; 65621308Sache } 65721308Sache } 65821308Sache 65921308Sache FREE (subst_rhs); 66021308Sache subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len); 66121308Sache 66221308Sache /* If `&' appears in the rhs, it's supposed to be replaced 66321308Sache with the lhs. */ 66421308Sache if (member ('&', subst_rhs)) 66521308Sache postproc_subst_rhs (); 66621308Sache } 66721308Sache else 66821308Sache i += 2; 66921308Sache 67047558Sache /* If there is no lhs, the substitution can't succeed. */ 67147558Sache if (subst_lhs_len == 0) 67247558Sache { 67347558Sache *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST); 67447558Sache free (result); 67547558Sache free (temp); 67647558Sache return -1; 67747558Sache } 67847558Sache 67921308Sache l_temp = strlen (temp); 68021308Sache /* Ignore impossible cases. */ 68121308Sache if (subst_lhs_len > l_temp) 68221308Sache { 68321308Sache *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); 68421308Sache free (result); 68521308Sache free (temp); 68621308Sache return (-1); 68721308Sache } 68821308Sache 68921308Sache /* Find the first occurrence of THIS in TEMP. */ 69021308Sache si = 0; 69121308Sache for (failed = 1; (si + subst_lhs_len) <= l_temp; si++) 69221308Sache if (STREQN (temp+si, subst_lhs, subst_lhs_len)) 69321308Sache { 69421308Sache int len = subst_rhs_len - subst_lhs_len + l_temp; 69521308Sache new_event = xmalloc (1 + len); 69621308Sache strncpy (new_event, temp, si); 69721308Sache strncpy (new_event + si, subst_rhs, subst_rhs_len); 69821308Sache strncpy (new_event + si + subst_rhs_len, 69921308Sache temp + si + subst_lhs_len, 70021308Sache l_temp - (si + subst_lhs_len)); 70121308Sache new_event[len] = '\0'; 70221308Sache free (temp); 70321308Sache temp = new_event; 70421308Sache 70521308Sache failed = 0; 70621308Sache 70721308Sache if (substitute_globally) 70821308Sache { 70921308Sache si += subst_rhs_len; 71021308Sache l_temp = strlen (temp); 71121308Sache substitute_globally++; 71221308Sache continue; 71321308Sache } 71421308Sache else 71521308Sache break; 71621308Sache } 71721308Sache 71821308Sache if (substitute_globally > 1) 71921308Sache { 72021308Sache substitute_globally = 0; 72121308Sache continue; /* don't want to increment i */ 72221308Sache } 72321308Sache 72421308Sache if (failed == 0) 72521308Sache continue; /* don't want to increment i */ 72621308Sache 72721308Sache *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); 72821308Sache free (result); 72921308Sache free (temp); 73021308Sache return (-1); 73121308Sache } 73221308Sache } 73321308Sache i += 2; 73421308Sache } 73521308Sache /* Done with modfiers. */ 73621308Sache /* Believe it or not, we have to back the pointer up by one. */ 73721308Sache --i; 73821308Sache 73921308Sache if (want_quotes) 74021308Sache { 74121308Sache char *x; 74221308Sache 74321308Sache if (want_quotes == 'q') 74421308Sache x = single_quote (temp); 74521308Sache else if (want_quotes == 'x') 74621308Sache x = quote_breaks (temp); 74721308Sache else 74821308Sache x = savestring (temp); 74921308Sache 75021308Sache free (temp); 75121308Sache temp = x; 75221308Sache } 75321308Sache 75421308Sache n = strlen (temp); 75521308Sache if (n >= result_len) 75621308Sache result = xrealloc (result, n + 2); 75721308Sache strcpy (result, temp); 75821308Sache free (temp); 75921308Sache 76021308Sache *end_index_ptr = i; 76121308Sache *ret_string = result; 76221308Sache return (print_only); 76321308Sache} 76421308Sache 76521308Sache/* Expand the string STRING, placing the result into OUTPUT, a pointer 76621308Sache to a string. Returns: 76721308Sache 76821308Sache -1) If there was an error in expansion. 76921308Sache 0) If no expansions took place (or, if the only change in 77021308Sache the text was the de-slashifying of the history expansion 77121308Sache character) 77221308Sache 1) If expansions did take place 77321308Sache 2) If the `p' modifier was given and the caller should print the result 77421308Sache 77521308Sache If an error ocurred in expansion, then OUTPUT contains a descriptive 77621308Sache error message. */ 77721308Sache 77821308Sache#define ADD_STRING(s) \ 77921308Sache do \ 78021308Sache { \ 78121308Sache int sl = strlen (s); \ 78221308Sache j += sl; \ 78321308Sache if (j >= result_len) \ 78421308Sache { \ 78521308Sache while (j >= result_len) \ 78621308Sache result_len += 128; \ 78721308Sache result = xrealloc (result, result_len); \ 78821308Sache } \ 78921308Sache strcpy (result + j - sl, s); \ 79021308Sache } \ 79121308Sache while (0) 79221308Sache 79321308Sache#define ADD_CHAR(c) \ 79421308Sache do \ 79521308Sache { \ 79621308Sache if (j >= result_len - 1) \ 79721308Sache result = xrealloc (result, result_len += 64); \ 79821308Sache result[j++] = c; \ 79921308Sache result[j] = '\0'; \ 80021308Sache } \ 80121308Sache while (0) 80221308Sache 80321308Sacheint 80421308Sachehistory_expand (hstring, output) 80521308Sache char *hstring; 80621308Sache char **output; 80721308Sache{ 80821308Sache register int j; 80921308Sache int i, r, l, passc, cc, modified, eindex, only_printing; 81021308Sache char *string; 81121308Sache 81221308Sache /* The output string, and its length. */ 81321308Sache int result_len; 81421308Sache char *result; 81521308Sache 81621308Sache /* Used when adding the string. */ 81721308Sache char *temp; 81821308Sache 81921308Sache /* Setting the history expansion character to 0 inhibits all 82021308Sache history expansion. */ 82121308Sache if (history_expansion_char == 0) 82221308Sache { 82321308Sache *output = savestring (hstring); 82421308Sache return (0); 82521308Sache } 82621308Sache 82721308Sache /* Prepare the buffer for printing error messages. */ 82821308Sache result = xmalloc (result_len = 256); 82921308Sache result[0] = '\0'; 83021308Sache 83121308Sache only_printing = modified = 0; 83221308Sache l = strlen (hstring); 83321308Sache 83435486Sache /* Grovel the string. Only backslash and single quotes can quote the 83535486Sache history escape character. We also handle arg specifiers. */ 83621308Sache 83721308Sache /* Before we grovel forever, see if the history_expansion_char appears 83821308Sache anywhere within the text. */ 83921308Sache 84021308Sache /* The quick substitution character is a history expansion all right. That 84121308Sache is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact, 84221308Sache that is the substitution that we do. */ 84321308Sache if (hstring[0] == history_subst_char) 84421308Sache { 84521308Sache string = xmalloc (l + 5); 84621308Sache 84721308Sache string[0] = string[1] = history_expansion_char; 84821308Sache string[2] = ':'; 84921308Sache string[3] = 's'; 85021308Sache strcpy (string + 4, hstring); 85121308Sache l += 4; 85221308Sache } 85321308Sache else 85421308Sache { 85521308Sache string = hstring; 85621308Sache /* If not quick substitution, still maybe have to do expansion. */ 85721308Sache 85821308Sache /* `!' followed by one of the characters in history_no_expand_chars 85921308Sache is NOT an expansion. */ 86021308Sache for (i = 0; string[i]; i++) 86121308Sache { 86221308Sache cc = string[i + 1]; 86335486Sache /* The history_comment_char, if set, appearing that the beginning 86435486Sache of a word signifies that the rest of the line should not have 86535486Sache history expansion performed on it. 86635486Sache Skip the rest of the line and break out of the loop. */ 86735486Sache if (history_comment_char && string[i] == history_comment_char && 86835486Sache (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS))) 86921308Sache { 87035486Sache while (string[i]) 87135486Sache i++; 87235486Sache break; 87335486Sache } 87435486Sache else if (string[i] == history_expansion_char) 87535486Sache { 87621308Sache if (!cc || member (cc, history_no_expand_chars)) 87721308Sache continue; 87826497Sache /* If the calling application has set 87926497Sache history_inhibit_expansion_function to a function that checks 88026497Sache for special cases that should not be history expanded, 88126497Sache call the function and skip the expansion if it returns a 88226497Sache non-zero value. */ 88326497Sache else if (history_inhibit_expansion_function && 88426497Sache (*history_inhibit_expansion_function) (string, i)) 88521308Sache continue; 88621308Sache else 88721308Sache break; 88821308Sache } 88935486Sache /* XXX - at some point, might want to extend this to handle 89035486Sache double quotes as well. */ 89121308Sache else if (history_quotes_inhibit_expansion && string[i] == '\'') 89221308Sache { 89321308Sache /* If this is bash, single quotes inhibit history expansion. */ 89421308Sache i++; 89521308Sache hist_string_extract_single_quoted (string, &i); 89621308Sache } 89721308Sache else if (history_quotes_inhibit_expansion && string[i] == '\\') 89821308Sache { 89921308Sache /* If this is bash, allow backslashes to quote single 90021308Sache quotes and the history expansion character. */ 90121308Sache if (cc == '\'' || cc == history_expansion_char) 90221308Sache i++; 90321308Sache } 90421308Sache } 90521308Sache 90621308Sache if (string[i] != history_expansion_char) 90721308Sache { 90821308Sache free (result); 90921308Sache *output = savestring (string); 91021308Sache return (0); 91121308Sache } 91221308Sache } 91321308Sache 91421308Sache /* Extract and perform the substitution. */ 91521308Sache for (passc = i = j = 0; i < l; i++) 91621308Sache { 91721308Sache int tchar = string[i]; 91821308Sache 91921308Sache if (passc) 92021308Sache { 92121308Sache passc = 0; 92221308Sache ADD_CHAR (tchar); 92321308Sache continue; 92421308Sache } 92521308Sache 92621308Sache if (tchar == history_expansion_char) 92721308Sache tchar = -3; 92835486Sache else if (tchar == history_comment_char) 92935486Sache tchar = -2; 93021308Sache 93121308Sache switch (tchar) 93221308Sache { 93321308Sache default: 93421308Sache ADD_CHAR (string[i]); 93521308Sache break; 93621308Sache 93721308Sache case '\\': 93821308Sache passc++; 93921308Sache ADD_CHAR (tchar); 94021308Sache break; 94121308Sache 94221308Sache case '\'': 94321308Sache { 94421308Sache /* If history_quotes_inhibit_expansion is set, single quotes 94521308Sache inhibit history expansion. */ 94621308Sache if (history_quotes_inhibit_expansion) 94721308Sache { 94821308Sache int quote, slen; 94921308Sache 95021308Sache quote = i++; 95121308Sache hist_string_extract_single_quoted (string, &i); 95221308Sache 95321308Sache slen = i - quote + 2; 95421308Sache temp = xmalloc (slen); 95521308Sache strncpy (temp, string + quote, slen); 95621308Sache temp[slen - 1] = '\0'; 95721308Sache ADD_STRING (temp); 95821308Sache free (temp); 95921308Sache } 96021308Sache else 96121308Sache ADD_CHAR (string[i]); 96221308Sache break; 96321308Sache } 96421308Sache 96535486Sache case -2: /* history_comment_char */ 96635486Sache if (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS)) 96735486Sache { 96835486Sache temp = xmalloc (l - i + 1); 96935486Sache strcpy (temp, string + i); 97035486Sache ADD_STRING (temp); 97135486Sache free (temp); 97235486Sache i = l; 97335486Sache } 97435486Sache else 97535486Sache ADD_CHAR (string[i]); 97635486Sache break; 97735486Sache 97821308Sache case -3: /* history_expansion_char */ 97921308Sache cc = string[i + 1]; 98021308Sache 98121308Sache /* If the history_expansion_char is followed by one of the 98221308Sache characters in history_no_expand_chars, then it is not a 98321308Sache candidate for expansion of any kind. */ 98421308Sache if (member (cc, history_no_expand_chars)) 98521308Sache { 98621308Sache ADD_CHAR (string[i]); 98721308Sache break; 98821308Sache } 98921308Sache 99021308Sache#if defined (NO_BANG_HASH_MODIFIERS) 99121308Sache /* There is something that is listed as a `word specifier' in csh 99221308Sache documentation which means `the expanded text to this point'. 99321308Sache That is not a word specifier, it is an event specifier. If we 99421308Sache don't want to allow modifiers with `!#', just stick the current 99521308Sache output line in again. */ 99621308Sache if (cc == '#') 99721308Sache { 99821308Sache if (result) 99921308Sache { 100021308Sache temp = xmalloc (1 + strlen (result)); 100121308Sache strcpy (temp, result); 100221308Sache ADD_STRING (temp); 100321308Sache free (temp); 100421308Sache } 100521308Sache i++; 100621308Sache break; 100721308Sache } 100821308Sache#endif 100921308Sache 101021308Sache r = history_expand_internal (string, i, &eindex, &temp, result); 101121308Sache if (r < 0) 101221308Sache { 101321308Sache *output = temp; 101421308Sache free (result); 101521308Sache if (string != hstring) 101621308Sache free (string); 101721308Sache return -1; 101821308Sache } 101921308Sache else 102021308Sache { 102121308Sache if (temp) 102221308Sache { 102321308Sache modified++; 102421308Sache if (*temp) 102521308Sache ADD_STRING (temp); 102621308Sache free (temp); 102721308Sache } 102821308Sache only_printing = r == 1; 102921308Sache i = eindex; 103021308Sache } 103121308Sache break; 103221308Sache } 103321308Sache } 103421308Sache 103521308Sache *output = result; 103621308Sache if (string != hstring) 103721308Sache free (string); 103821308Sache 103921308Sache if (only_printing) 104021308Sache { 104121308Sache add_history (result); 104221308Sache return (2); 104321308Sache } 104421308Sache 104521308Sache return (modified != 0); 104621308Sache} 104721308Sache 104821308Sache/* Return a consed string which is the word specified in SPEC, and found 104921308Sache in FROM. NULL is returned if there is no spec. The address of 105021308Sache ERROR_POINTER is returned if the word specified cannot be found. 105121308Sache CALLER_INDEX is the offset in SPEC to start looking; it is updated 105221308Sache to point to just after the last character parsed. */ 105321308Sachestatic char * 105421308Sacheget_history_word_specifier (spec, from, caller_index) 105521308Sache char *spec, *from; 105621308Sache int *caller_index; 105721308Sache{ 105821308Sache register int i = *caller_index; 105921308Sache int first, last; 106021308Sache int expecting_word_spec = 0; 106121308Sache char *result; 106221308Sache 106321308Sache /* The range of words to return doesn't exist yet. */ 106421308Sache first = last = 0; 106521308Sache result = (char *)NULL; 106621308Sache 106721308Sache /* If we found a colon, then this *must* be a word specification. If 106821308Sache it isn't, then it is an error. */ 106921308Sache if (spec[i] == ':') 107021308Sache { 107121308Sache i++; 107221308Sache expecting_word_spec++; 107321308Sache } 107421308Sache 107521308Sache /* Handle special cases first. */ 107621308Sache 107721308Sache /* `%' is the word last searched for. */ 107821308Sache if (spec[i] == '%') 107921308Sache { 108021308Sache *caller_index = i + 1; 108121308Sache return (search_match ? savestring (search_match) : savestring ("")); 108221308Sache } 108321308Sache 108421308Sache /* `*' matches all of the arguments, but not the command. */ 108521308Sache if (spec[i] == '*') 108621308Sache { 108721308Sache *caller_index = i + 1; 108821308Sache result = history_arg_extract (1, '$', from); 108921308Sache return (result ? result : savestring ("")); 109021308Sache } 109121308Sache 109221308Sache /* `$' is last arg. */ 109321308Sache if (spec[i] == '$') 109421308Sache { 109521308Sache *caller_index = i + 1; 109621308Sache return (history_arg_extract ('$', '$', from)); 109721308Sache } 109821308Sache 109921308Sache /* Try to get FIRST and LAST figured out. */ 110021308Sache 110121308Sache if (spec[i] == '-') 110221308Sache first = 0; 110321308Sache else if (spec[i] == '^') 110421308Sache first = 1; 110521308Sache else if (_rl_digit_p (spec[i]) && expecting_word_spec) 110621308Sache { 110721308Sache for (first = 0; _rl_digit_p (spec[i]); i++) 110821308Sache first = (first * 10) + _rl_digit_value (spec[i]); 110921308Sache } 111021308Sache else 111121308Sache return ((char *)NULL); /* no valid `first' for word specifier */ 111221308Sache 111321308Sache if (spec[i] == '^' || spec[i] == '*') 111421308Sache { 111521308Sache last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */ 111621308Sache i++; 111721308Sache } 111821308Sache else if (spec[i] != '-') 111921308Sache last = first; 112021308Sache else 112121308Sache { 112221308Sache i++; 112321308Sache 112421308Sache if (_rl_digit_p (spec[i])) 112521308Sache { 112621308Sache for (last = 0; _rl_digit_p (spec[i]); i++) 112721308Sache last = (last * 10) + _rl_digit_value (spec[i]); 112821308Sache } 112921308Sache else if (spec[i] == '$') 113021308Sache { 113121308Sache i++; 113221308Sache last = '$'; 113321308Sache } 113421308Sache else if (!spec[i] || spec[i] == ':') /* could be modifier separator */ 113521308Sache last = -1; /* x- abbreviates x-$ omitting word `$' */ 113621308Sache } 113721308Sache 113821308Sache *caller_index = i; 113921308Sache 114021308Sache if (last >= first || last == '$' || last < 0) 114121308Sache result = history_arg_extract (first, last, from); 114221308Sache 114321308Sache return (result ? result : (char *)&error_pointer); 114421308Sache} 114521308Sache 114621308Sache/* Extract the args specified, starting at FIRST, and ending at LAST. 114721308Sache The args are taken from STRING. If either FIRST or LAST is < 0, 114821308Sache then make that arg count from the right (subtract from the number of 114921308Sache tokens, so that FIRST = -1 means the next to last token on the line). 115021308Sache If LAST is `$' the last arg from STRING is used. */ 115121308Sachechar * 115221308Sachehistory_arg_extract (first, last, string) 115321308Sache int first, last; 115421308Sache char *string; 115521308Sache{ 115621308Sache register int i, len; 115721308Sache char *result; 115821308Sache int size, offset; 115921308Sache char **list; 116021308Sache 116121308Sache /* XXX - think about making history_tokenize return a struct array, 116221308Sache each struct in array being a string and a length to avoid the 116321308Sache calls to strlen below. */ 116421308Sache if ((list = history_tokenize (string)) == NULL) 116521308Sache return ((char *)NULL); 116621308Sache 116721308Sache for (len = 0; list[len]; len++) 116821308Sache ; 116921308Sache 117021308Sache if (last < 0) 117121308Sache last = len + last - 1; 117221308Sache 117321308Sache if (first < 0) 117421308Sache first = len + first - 1; 117521308Sache 117621308Sache if (last == '$') 117721308Sache last = len - 1; 117821308Sache 117921308Sache if (first == '$') 118021308Sache first = len - 1; 118121308Sache 118221308Sache last++; 118321308Sache 118421308Sache if (first >= len || last > len || first < 0 || last < 0 || first > last) 118521308Sache result = ((char *)NULL); 118621308Sache else 118721308Sache { 118821308Sache for (size = 0, i = first; i < last; i++) 118921308Sache size += strlen (list[i]) + 1; 119021308Sache result = xmalloc (size + 1); 119121308Sache result[0] = '\0'; 119221308Sache 119321308Sache for (i = first, offset = 0; i < last; i++) 119421308Sache { 119521308Sache strcpy (result + offset, list[i]); 119621308Sache offset += strlen (list[i]); 119721308Sache if (i + 1 < last) 119821308Sache { 119921308Sache result[offset++] = ' '; 120021308Sache result[offset] = 0; 120121308Sache } 120221308Sache } 120321308Sache } 120421308Sache 120521308Sache for (i = 0; i < len; i++) 120621308Sache free (list[i]); 120721308Sache free (list); 120821308Sache 120921308Sache return (result); 121021308Sache} 121121308Sache 121221308Sache#define slashify_in_quotes "\\`\"$" 121321308Sache 121421308Sache/* Parse STRING into tokens and return an array of strings. If WIND is 121521308Sache not -1 and INDP is not null, we also want the word surrounding index 121621308Sache WIND. The position in the returned array of strings is returned in 121721308Sache *INDP. */ 121821308Sachestatic char ** 121921308Sachehistory_tokenize_internal (string, wind, indp) 122021308Sache char *string; 122121308Sache int wind, *indp; 122221308Sache{ 122321308Sache char **result; 122421308Sache register int i, start, result_index, size; 122521308Sache int len, delimiter; 122621308Sache 122721308Sache /* Get a token, and stuff it into RESULT. The tokens are split 122821308Sache exactly where the shell would split them. */ 122921308Sache for (i = result_index = size = 0, result = (char **)NULL; string[i]; ) 123021308Sache { 123121308Sache delimiter = 0; 123221308Sache 123321308Sache /* Skip leading whitespace. */ 123421308Sache for (; string[i] && whitespace (string[i]); i++) 123521308Sache ; 123621308Sache if (string[i] == 0 || string[i] == history_comment_char) 123721308Sache return (result); 123821308Sache 123921308Sache start = i; 124021308Sache 124121308Sache if (member (string[i], "()\n")) 124221308Sache { 124321308Sache i++; 124421308Sache goto got_token; 124521308Sache } 124621308Sache 124721308Sache if (member (string[i], "<>;&|$")) 124821308Sache { 124921308Sache int peek = string[i + 1]; 125021308Sache 125121308Sache if (peek == string[i] && peek != '$') 125221308Sache { 125321308Sache if (peek == '<' && string[i + 2] == '-') 125421308Sache i++; 125521308Sache i += 2; 125621308Sache goto got_token; 125721308Sache } 125821308Sache else 125921308Sache { 126021308Sache if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || 126121308Sache ((peek == '>') && (string[i] == '&')) || 126221308Sache ((peek == '(') && (string[i] == '$'))) 126321308Sache { 126421308Sache i += 2; 126521308Sache goto got_token; 126621308Sache } 126721308Sache } 126821308Sache if (string[i] != '$') 126921308Sache { 127021308Sache i++; 127121308Sache goto got_token; 127221308Sache } 127321308Sache } 127421308Sache 127521308Sache /* Get word from string + i; */ 127621308Sache 127735486Sache if (member (string[i], HISTORY_QUOTE_CHARACTERS)) 127821308Sache delimiter = string[i++]; 127921308Sache 128021308Sache for (; string[i]; i++) 128121308Sache { 128221308Sache if (string[i] == '\\' && string[i + 1] == '\n') 128321308Sache { 128421308Sache i++; 128521308Sache continue; 128621308Sache } 128721308Sache 128821308Sache if (string[i] == '\\' && delimiter != '\'' && 128921308Sache (delimiter != '"' || member (string[i], slashify_in_quotes))) 129021308Sache { 129121308Sache i++; 129221308Sache continue; 129321308Sache } 129421308Sache 129521308Sache if (delimiter && string[i] == delimiter) 129621308Sache { 129721308Sache delimiter = 0; 129821308Sache continue; 129921308Sache } 130021308Sache 130135486Sache if (!delimiter && (member (string[i], HISTORY_WORD_DELIMITERS))) 130221308Sache break; 130321308Sache 130435486Sache if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)) 130521308Sache delimiter = string[i]; 130621308Sache } 130721308Sache 130821308Sache got_token: 130921308Sache 131021308Sache /* If we are looking for the word in which the character at a 131121308Sache particular index falls, remember it. */ 131221308Sache if (indp && wind != -1 && wind >= start && wind < i) 131321308Sache *indp = result_index; 131421308Sache 131521308Sache len = i - start; 131621308Sache if (result_index + 2 >= size) 131721308Sache result = (char **)xrealloc (result, ((size += 10) * sizeof (char *))); 131821308Sache result[result_index] = xmalloc (1 + len); 131921308Sache strncpy (result[result_index], string + start, len); 132021308Sache result[result_index][len] = '\0'; 132121308Sache result[++result_index] = (char *)NULL; 132221308Sache } 132321308Sache 132421308Sache return (result); 132521308Sache} 132621308Sache 132721308Sache/* Return an array of tokens, much as the shell might. The tokens are 132821308Sache parsed out of STRING. */ 132921308Sachechar ** 133021308Sachehistory_tokenize (string) 133121308Sache char *string; 133221308Sache{ 133321308Sache return (history_tokenize_internal (string, -1, (int *)NULL)); 133421308Sache} 133521308Sache 133621308Sache/* Find and return the word which contains the character at index IND 133721308Sache in the history line LINE. Used to save the word matched by the 133821308Sache last history !?string? search. */ 133921308Sachestatic char * 134021308Sachehistory_find_word (line, ind) 134121308Sache char *line; 134221308Sache int ind; 134321308Sache{ 134421308Sache char **words, *s; 134521308Sache int i, wind; 134621308Sache 134721308Sache words = history_tokenize_internal (line, ind, &wind); 134821308Sache if (wind == -1) 134921308Sache return ((char *)NULL); 135021308Sache s = words[wind]; 135121308Sache for (i = 0; i < wind; i++) 135221308Sache free (words[i]); 135321308Sache for (i = wind + 1; words[i]; i++) 135421308Sache free (words[i]); 135521308Sache free (words); 135621308Sache return s; 135721308Sache} 1358