histexpand.c revision 119610
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 44119610Sache#include "rlmbutil.h" 4521308Sache 4621308Sache#include "history.h" 4721308Sache#include "histlib.h" 4821308Sache 4958310Sache#include "rlshell.h" 5058310Sache#include "xmalloc.h" 5158310Sache 5235486Sache#define HISTORY_WORD_DELIMITERS " \t\n;&()|<>" 5335486Sache#define HISTORY_QUOTE_CHARACTERS "\"'`" 5435486Sache 55119610Sachetypedef int _hist_search_func_t PARAMS((const char *, int)); 5675406Sache 57119610Sacheextern int rl_byte_oriented; /* declared in mbutil.c */ 58119610Sache 5921308Sachestatic char error_pointer; 6021308Sache 6121308Sachestatic char *subst_lhs; 6221308Sachestatic char *subst_rhs; 6321308Sachestatic int subst_lhs_len; 6421308Sachestatic int subst_rhs_len; 6521308Sache 66119610Sachestatic char *get_history_word_specifier PARAMS((char *, char *, int *)); 67119610Sachestatic char *history_find_word PARAMS((char *, int)); 6821308Sache 69119610Sachestatic char *quote_breaks PARAMS((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 9475406Sache/* Used to split words by history_tokenize_internal. */ 9575406Sachechar *history_word_delimiters = HISTORY_WORD_DELIMITERS; 9675406Sache 9726497Sache/* If set, this points to a function that is called to verify that a 9826497Sache particular history expansion should be performed. */ 9975406Sacherl_linebuf_func_t *history_inhibit_expansion_function; 10026497Sache 10121308Sache/* **************************************************************** */ 10221308Sache/* */ 10321308Sache/* History Expansion */ 10421308Sache/* */ 10521308Sache/* **************************************************************** */ 10621308Sache 10721308Sache/* Hairy history expansion on text, not tokens. This is of general 10821308Sache use, and thus belongs in this library. */ 10921308Sache 11021308Sache/* The last string searched for by a !?string? search. */ 11121308Sachestatic char *search_string; 11221308Sache 11321308Sache/* The last string matched by a !?string? search. */ 11421308Sachestatic char *search_match; 11521308Sache 11621308Sache/* Return the event specified at TEXT + OFFSET modifying OFFSET to 11721308Sache point to after the event specifier. Just a pointer to the history 11821308Sache line is returned; NULL is returned in the event of a bad specifier. 11921308Sache You pass STRING with *INDEX equal to the history_expansion_char that 12021308Sache begins this specification. 12121308Sache DELIMITING_QUOTE is a character that is allowed to end the string 12221308Sache specification for what to search for in addition to the normal 12321308Sache characters `:', ` ', `\t', `\n', and sometimes `?'. 12421308Sache So you might call this function like: 12521308Sache line = get_history_event ("!echo:p", &index, 0); */ 12621308Sachechar * 12721308Sacheget_history_event (string, caller_index, delimiting_quote) 12875406Sache const char *string; 12921308Sache int *caller_index; 13021308Sache int delimiting_quote; 13121308Sache{ 13221308Sache register int i; 13321308Sache register char c; 13421308Sache HIST_ENTRY *entry; 13521308Sache int which, sign, local_index, substring_okay; 13675406Sache _hist_search_func_t *search_func; 13721308Sache char *temp; 13821308Sache 13921308Sache /* The event can be specified in a number of ways. 14021308Sache 14121308Sache !! the previous command 14221308Sache !n command line N 14321308Sache !-n current command-line minus N 14421308Sache !str the most recent command starting with STR 14521308Sache !?str[?] 14621308Sache the most recent command containing STR 14721308Sache 14821308Sache All values N are determined via HISTORY_BASE. */ 14921308Sache 15021308Sache i = *caller_index; 15121308Sache 15221308Sache if (string[i] != history_expansion_char) 15321308Sache return ((char *)NULL); 15421308Sache 15521308Sache /* Move on to the specification. */ 15621308Sache i++; 15721308Sache 15821308Sache sign = 1; 15921308Sache substring_okay = 0; 16021308Sache 16121308Sache#define RETURN_ENTRY(e, w) \ 16221308Sache return ((e = history_get (w)) ? e->line : (char *)NULL) 16321308Sache 16421308Sache /* Handle !! case. */ 16521308Sache if (string[i] == history_expansion_char) 16621308Sache { 16721308Sache i++; 16821308Sache which = history_base + (history_length - 1); 16921308Sache *caller_index = i; 17021308Sache RETURN_ENTRY (entry, which); 17121308Sache } 17221308Sache 17321308Sache /* Hack case of numeric line specification. */ 17421308Sache if (string[i] == '-') 17521308Sache { 17621308Sache sign = -1; 17721308Sache i++; 17821308Sache } 17921308Sache 18021308Sache if (_rl_digit_p (string[i])) 18121308Sache { 18221308Sache /* Get the extent of the digits and compute the value. */ 18321308Sache for (which = 0; _rl_digit_p (string[i]); i++) 18421308Sache which = (which * 10) + _rl_digit_value (string[i]); 18521308Sache 18621308Sache *caller_index = i; 18721308Sache 18821308Sache if (sign < 0) 18921308Sache which = (history_length + history_base) - which; 19021308Sache 19121308Sache RETURN_ENTRY (entry, which); 19221308Sache } 19321308Sache 19421308Sache /* This must be something to search for. If the spec begins with 19521308Sache a '?', then the string may be anywhere on the line. Otherwise, 19621308Sache the string must be found at the start of a line. */ 19721308Sache if (string[i] == '?') 19821308Sache { 19921308Sache substring_okay++; 20021308Sache i++; 20121308Sache } 20221308Sache 20321308Sache /* Only a closing `?' or a newline delimit a substring search string. */ 20421308Sache for (local_index = i; c = string[i]; i++) 205119610Sache#if defined (HANDLE_MULTIBYTE) 206119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 207119610Sache { 208119610Sache int v; 209119610Sache mbstate_t ps; 21021308Sache 211119610Sache memset (&ps, 0, sizeof (mbstate_t)); 212119610Sache /* These produce warnings because we're passing a const string to a 213119610Sache function that takes a non-const string. */ 214119610Sache _rl_adjust_point (string, i, &ps); 215119610Sache if ((v = _rl_get_char_len (string + i, &ps)) > 1) 216119610Sache { 217119610Sache i += v - 1; 218119610Sache continue; 219119610Sache } 220119610Sache } 221119610Sache else 222119610Sache#endif /* HANDLE_MULTIBYTE */ 223119610Sache if ((!substring_okay && (whitespace (c) || c == ':' || 224119610Sache (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) || 225119610Sache string[i] == delimiting_quote)) || 226119610Sache string[i] == '\n' || 227119610Sache (substring_okay && string[i] == '?')) 228119610Sache break; 229119610Sache 23021308Sache which = i - local_index; 231119610Sache temp = (char *)xmalloc (1 + which); 23221308Sache if (which) 23321308Sache strncpy (temp, string + local_index, which); 23421308Sache temp[which] = '\0'; 23521308Sache 23621308Sache if (substring_okay && string[i] == '?') 23721308Sache i++; 23821308Sache 23921308Sache *caller_index = i; 24021308Sache 24121308Sache#define FAIL_SEARCH() \ 24221308Sache do { \ 24321308Sache history_offset = history_length; free (temp) ; return (char *)NULL; \ 24421308Sache } while (0) 24521308Sache 24621308Sache /* If there is no search string, try to use the previous search string, 24721308Sache if one exists. If not, fail immediately. */ 24821308Sache if (*temp == '\0' && substring_okay) 24921308Sache { 25021308Sache if (search_string) 25121308Sache { 25221308Sache free (temp); 25321308Sache temp = savestring (search_string); 25421308Sache } 25521308Sache else 25621308Sache FAIL_SEARCH (); 25721308Sache } 25821308Sache 25921308Sache search_func = substring_okay ? history_search : history_search_prefix; 26021308Sache while (1) 26121308Sache { 26221308Sache local_index = (*search_func) (temp, -1); 26321308Sache 26421308Sache if (local_index < 0) 26521308Sache FAIL_SEARCH (); 26621308Sache 26721308Sache if (local_index == 0 || substring_okay) 26821308Sache { 26921308Sache entry = current_history (); 27021308Sache history_offset = history_length; 27121308Sache 27221308Sache /* If this was a substring search, then remember the 27321308Sache string that we matched for word substitution. */ 27421308Sache if (substring_okay) 27521308Sache { 27621308Sache FREE (search_string); 27721308Sache search_string = temp; 27821308Sache 27921308Sache FREE (search_match); 28021308Sache search_match = history_find_word (entry->line, local_index); 28121308Sache } 28221308Sache else 28321308Sache free (temp); 28421308Sache 28521308Sache return (entry->line); 28621308Sache } 28721308Sache 28821308Sache if (history_offset) 28921308Sache history_offset--; 29021308Sache else 29121308Sache FAIL_SEARCH (); 29221308Sache } 29321308Sache#undef FAIL_SEARCH 29421308Sache#undef RETURN_ENTRY 29521308Sache} 29621308Sache 29721308Sache/* Function for extracting single-quoted strings. Used for inhibiting 29821308Sache history expansion within single quotes. */ 29921308Sache 30021308Sache/* Extract the contents of STRING as if it is enclosed in single quotes. 30121308Sache SINDEX, when passed in, is the offset of the character immediately 30221308Sache following the opening single quote; on exit, SINDEX is left pointing 30321308Sache to the closing single quote. */ 30421308Sachestatic void 30521308Sachehist_string_extract_single_quoted (string, sindex) 30621308Sache char *string; 30721308Sache int *sindex; 30821308Sache{ 30921308Sache register int i; 31021308Sache 31121308Sache for (i = *sindex; string[i] && string[i] != '\''; i++) 31221308Sache ; 31321308Sache 31421308Sache *sindex = i; 31521308Sache} 31621308Sache 31721308Sachestatic char * 31821308Sachequote_breaks (s) 31921308Sache char *s; 32021308Sache{ 32121308Sache register char *p, *r; 32221308Sache char *ret; 32321308Sache int len = 3; 32421308Sache 32521308Sache for (p = s; p && *p; p++, len++) 32621308Sache { 32721308Sache if (*p == '\'') 32821308Sache len += 3; 32921308Sache else if (whitespace (*p) || *p == '\n') 33021308Sache len += 2; 33121308Sache } 33221308Sache 333119610Sache r = ret = (char *)xmalloc (len); 33421308Sache *r++ = '\''; 33521308Sache for (p = s; p && *p; ) 33621308Sache { 33721308Sache if (*p == '\'') 33821308Sache { 33921308Sache *r++ = '\''; 34021308Sache *r++ = '\\'; 34121308Sache *r++ = '\''; 34221308Sache *r++ = '\''; 34321308Sache p++; 34421308Sache } 34521308Sache else if (whitespace (*p) || *p == '\n') 34621308Sache { 34721308Sache *r++ = '\''; 34821308Sache *r++ = *p++; 34921308Sache *r++ = '\''; 35021308Sache } 35121308Sache else 35221308Sache *r++ = *p++; 35321308Sache } 35421308Sache *r++ = '\''; 35521308Sache *r = '\0'; 35621308Sache return ret; 35721308Sache} 35821308Sache 35921308Sachestatic char * 36021308Sachehist_error(s, start, current, errtype) 36121308Sache char *s; 36221308Sache int start, current, errtype; 36321308Sache{ 36475406Sache char *temp; 36575406Sache const char *emsg; 36621308Sache int ll, elen; 36721308Sache 36821308Sache ll = current - start; 36921308Sache 37021308Sache switch (errtype) 37121308Sache { 37221308Sache case EVENT_NOT_FOUND: 37321308Sache emsg = "event not found"; 37421308Sache elen = 15; 37521308Sache break; 37621308Sache case BAD_WORD_SPEC: 37721308Sache emsg = "bad word specifier"; 37821308Sache elen = 18; 37921308Sache break; 38021308Sache case SUBST_FAILED: 38121308Sache emsg = "substitution failed"; 38221308Sache elen = 19; 38321308Sache break; 38421308Sache case BAD_MODIFIER: 38521308Sache emsg = "unrecognized history modifier"; 38621308Sache elen = 29; 38721308Sache break; 38847558Sache case NO_PREV_SUBST: 38947558Sache emsg = "no previous substitution"; 39047558Sache elen = 24; 39147558Sache break; 39221308Sache default: 39321308Sache emsg = "unknown expansion error"; 39421308Sache elen = 23; 39521308Sache break; 39621308Sache } 39721308Sache 398119610Sache temp = (char *)xmalloc (ll + elen + 3); 39921308Sache strncpy (temp, s + start, ll); 40021308Sache temp[ll] = ':'; 40121308Sache temp[ll + 1] = ' '; 40221308Sache strcpy (temp + ll + 2, emsg); 40321308Sache return (temp); 40421308Sache} 40521308Sache 40621308Sache/* Get a history substitution string from STR starting at *IPTR 40721308Sache and return it. The length is returned in LENPTR. 40821308Sache 40921308Sache A backslash can quote the delimiter. If the string is the 41021308Sache empty string, the previous pattern is used. If there is 41121308Sache no previous pattern for the lhs, the last history search 41221308Sache string is used. 41321308Sache 41421308Sache If IS_RHS is 1, we ignore empty strings and set the pattern 41521308Sache to "" anyway. subst_lhs is not changed if the lhs is empty; 41621308Sache subst_rhs is allowed to be set to the empty string. */ 41721308Sache 41821308Sachestatic char * 41921308Sacheget_subst_pattern (str, iptr, delimiter, is_rhs, lenptr) 42021308Sache char *str; 42121308Sache int *iptr, delimiter, is_rhs, *lenptr; 42221308Sache{ 42321308Sache register int si, i, j, k; 424119610Sache char *s; 425119610Sache#if defined (HANDLE_MULTIBYTE) 426119610Sache mbstate_t ps; 427119610Sache#endif 42821308Sache 429119610Sache s = (char *)NULL; 43021308Sache i = *iptr; 43121308Sache 432119610Sache#if defined (HANDLE_MULTIBYTE) 433119610Sache memset (&ps, 0, sizeof (mbstate_t)); 434119610Sache _rl_adjust_point (str, i, &ps); 435119610Sache#endif 436119610Sache 43721308Sache for (si = i; str[si] && str[si] != delimiter; si++) 438119610Sache#if defined (HANDLE_MULTIBYTE) 439119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 440119610Sache { 441119610Sache int v; 442119610Sache if ((v = _rl_get_char_len (str + si, &ps)) > 1) 443119610Sache si += v - 1; 444119610Sache else if (str[si] == '\\' && str[si + 1] == delimiter) 445119610Sache si++; 446119610Sache } 447119610Sache else 448119610Sache#endif /* HANDLE_MULTIBYTE */ 449119610Sache if (str[si] == '\\' && str[si + 1] == delimiter) 450119610Sache si++; 45121308Sache 45221308Sache if (si > i || is_rhs) 45321308Sache { 454119610Sache s = (char *)xmalloc (si - i + 1); 45521308Sache for (j = 0, k = i; k < si; j++, k++) 45621308Sache { 45721308Sache /* Remove a backslash quoting the search string delimiter. */ 45821308Sache if (str[k] == '\\' && str[k + 1] == delimiter) 45921308Sache k++; 46021308Sache s[j] = str[k]; 46121308Sache } 46221308Sache s[j] = '\0'; 46321308Sache if (lenptr) 46421308Sache *lenptr = j; 46521308Sache } 46621308Sache 46721308Sache i = si; 46821308Sache if (str[i]) 46921308Sache i++; 47021308Sache *iptr = i; 47121308Sache 47221308Sache return s; 47321308Sache} 47421308Sache 47521308Sachestatic void 47621308Sachepostproc_subst_rhs () 47721308Sache{ 47821308Sache char *new; 47921308Sache int i, j, new_size; 48021308Sache 481119610Sache new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len); 48221308Sache for (i = j = 0; i < subst_rhs_len; i++) 48321308Sache { 48421308Sache if (subst_rhs[i] == '&') 48521308Sache { 48621308Sache if (j + subst_lhs_len >= new_size) 487119610Sache new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len)); 48821308Sache strcpy (new + j, subst_lhs); 48921308Sache j += subst_lhs_len; 49021308Sache } 49121308Sache else 49221308Sache { 49321308Sache /* a single backslash protects the `&' from lhs interpolation */ 49421308Sache if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&') 49521308Sache i++; 49621308Sache if (j >= new_size) 497119610Sache new = (char *)xrealloc (new, new_size *= 2); 49821308Sache new[j++] = subst_rhs[i]; 49921308Sache } 50021308Sache } 50121308Sache new[j] = '\0'; 50221308Sache free (subst_rhs); 50321308Sache subst_rhs = new; 50421308Sache subst_rhs_len = j; 50521308Sache} 50621308Sache 50721308Sache/* Expand the bulk of a history specifier starting at STRING[START]. 50821308Sache Returns 0 if everything is OK, -1 if an error occurred, and 1 50921308Sache if the `p' modifier was supplied and the caller should just print 51021308Sache the returned string. Returns the new index into string in 51121308Sache *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */ 51221308Sachestatic int 51321308Sachehistory_expand_internal (string, start, end_index_ptr, ret_string, current_line) 51421308Sache char *string; 51521308Sache int start, *end_index_ptr; 51621308Sache char **ret_string; 51721308Sache char *current_line; /* for !# */ 51821308Sache{ 51921308Sache int i, n, starting_index; 52021308Sache int substitute_globally, want_quotes, print_only; 52121308Sache char *event, *temp, *result, *tstr, *t, c, *word_spec; 52221308Sache int result_len; 523119610Sache#if defined (HANDLE_MULTIBYTE) 524119610Sache mbstate_t ps; 52521308Sache 526119610Sache memset (&ps, 0, sizeof (mbstate_t)); 527119610Sache#endif 52821308Sache 529119610Sache result = (char *)xmalloc (result_len = 128); 530119610Sache 53121308Sache i = start; 53221308Sache 53321308Sache /* If it is followed by something that starts a word specifier, 53421308Sache then !! is implied as the event specifier. */ 53521308Sache 53621308Sache if (member (string[i + 1], ":$*%^")) 53721308Sache { 53821308Sache char fake_s[3]; 53921308Sache int fake_i = 0; 54021308Sache i++; 54121308Sache fake_s[0] = fake_s[1] = history_expansion_char; 54221308Sache fake_s[2] = '\0'; 54321308Sache event = get_history_event (fake_s, &fake_i, 0); 54421308Sache } 54521308Sache else if (string[i + 1] == '#') 54621308Sache { 54721308Sache i += 2; 54821308Sache event = current_line; 54921308Sache } 55021308Sache else 55121308Sache { 55221308Sache int quoted_search_delimiter = 0; 55321308Sache 55421308Sache /* If the character before this `!' is a double or single 55521308Sache quote, then this expansion takes place inside of the 55621308Sache quoted string. If we have to search for some text ("!foo"), 55721308Sache allow the delimiter to end the search string. */ 558119610Sache#if defined (HANDLE_MULTIBYTE) 559119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 560119610Sache { 561119610Sache int c, l; 562119610Sache l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY); 563119610Sache c = string[l]; 564119610Sache /* XXX - original patch had i - 1 ??? If i == 0 it would fail. */ 565119610Sache if (i && (c == '\'' || c == '"')) 566119610Sache quoted_search_delimiter = c; 567119610Sache } 568119610Sache else 569119610Sache#endif /* HANDLE_MULTIBYTE */ 570119610Sache if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) 571119610Sache quoted_search_delimiter = string[i - 1]; 572119610Sache 57321308Sache event = get_history_event (string, &i, quoted_search_delimiter); 57421308Sache } 57521308Sache 57621308Sache if (event == 0) 57721308Sache { 57821308Sache *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND); 57921308Sache free (result); 58021308Sache return (-1); 58121308Sache } 58221308Sache 58321308Sache /* If a word specifier is found, then do what that requires. */ 58421308Sache starting_index = i; 58521308Sache word_spec = get_history_word_specifier (string, event, &i); 58621308Sache 58721308Sache /* There is no such thing as a `malformed word specifier'. However, 58821308Sache it is possible for a specifier that has no match. In that case, 58921308Sache we complain. */ 59021308Sache if (word_spec == (char *)&error_pointer) 59121308Sache { 59221308Sache *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC); 59321308Sache free (result); 59421308Sache return (-1); 59521308Sache } 59621308Sache 59721308Sache /* If no word specifier, than the thing of interest was the event. */ 59821308Sache temp = word_spec ? savestring (word_spec) : savestring (event); 59921308Sache FREE (word_spec); 60021308Sache 60121308Sache /* Perhaps there are other modifiers involved. Do what they say. */ 60221308Sache want_quotes = substitute_globally = print_only = 0; 60321308Sache starting_index = i; 60421308Sache 60521308Sache while (string[i] == ':') 60621308Sache { 60721308Sache c = string[i + 1]; 60821308Sache 60921308Sache if (c == 'g') 61021308Sache { 61121308Sache substitute_globally = 1; 61221308Sache i++; 61321308Sache c = string[i + 1]; 61421308Sache } 61521308Sache 61621308Sache switch (c) 61721308Sache { 61821308Sache default: 61921308Sache *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER); 62021308Sache free (result); 62121308Sache free (temp); 62221308Sache return -1; 62321308Sache 62421308Sache case 'q': 62521308Sache want_quotes = 'q'; 62621308Sache break; 62721308Sache 62821308Sache case 'x': 62921308Sache want_quotes = 'x'; 63021308Sache break; 63121308Sache 63221308Sache /* :p means make this the last executed line. So we 63321308Sache return an error state after adding this line to the 63421308Sache history. */ 63521308Sache case 'p': 63621308Sache print_only++; 63721308Sache break; 63821308Sache 63921308Sache /* :t discards all but the last part of the pathname. */ 64021308Sache case 't': 64121308Sache tstr = strrchr (temp, '/'); 64221308Sache if (tstr) 64321308Sache { 64421308Sache tstr++; 64521308Sache t = savestring (tstr); 64621308Sache free (temp); 64721308Sache temp = t; 64821308Sache } 64921308Sache break; 65021308Sache 65121308Sache /* :h discards the last part of a pathname. */ 65221308Sache case 'h': 65321308Sache tstr = strrchr (temp, '/'); 65421308Sache if (tstr) 65521308Sache *tstr = '\0'; 65621308Sache break; 65721308Sache 65821308Sache /* :r discards the suffix. */ 65921308Sache case 'r': 66021308Sache tstr = strrchr (temp, '.'); 66121308Sache if (tstr) 66221308Sache *tstr = '\0'; 66321308Sache break; 66421308Sache 66521308Sache /* :e discards everything but the suffix. */ 66621308Sache case 'e': 66721308Sache tstr = strrchr (temp, '.'); 66821308Sache if (tstr) 66921308Sache { 67021308Sache t = savestring (tstr); 67121308Sache free (temp); 67221308Sache temp = t; 67321308Sache } 67421308Sache break; 67521308Sache 67621308Sache /* :s/this/that substitutes `that' for the first 67721308Sache occurrence of `this'. :gs/this/that substitutes `that' 67821308Sache for each occurrence of `this'. :& repeats the last 67921308Sache substitution. :g& repeats the last substitution 68021308Sache globally. */ 68121308Sache 68221308Sache case '&': 68321308Sache case 's': 68421308Sache { 68575406Sache char *new_event; 68621308Sache int delimiter, failed, si, l_temp; 68721308Sache 68821308Sache if (c == 's') 68921308Sache { 69021308Sache if (i + 2 < (int)strlen (string)) 691119610Sache { 692119610Sache#if defined (HANDLE_MULTIBYTE) 693119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 694119610Sache { 695119610Sache _rl_adjust_point (string, i + 2, &ps); 696119610Sache if (_rl_get_char_len (string + i + 2, &ps) > 1) 697119610Sache delimiter = 0; 698119610Sache else 699119610Sache delimiter = string[i + 2]; 700119610Sache } 701119610Sache else 702119610Sache#endif /* HANDLE_MULTIBYTE */ 703119610Sache delimiter = string[i + 2]; 704119610Sache } 70521308Sache else 70621308Sache break; /* no search delimiter */ 70721308Sache 70821308Sache i += 3; 70921308Sache 71021308Sache t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len); 71121308Sache /* An empty substitution lhs with no previous substitution 71221308Sache uses the last search string as the lhs. */ 71321308Sache if (t) 71421308Sache { 71521308Sache FREE (subst_lhs); 71621308Sache subst_lhs = t; 71721308Sache } 71821308Sache else if (!subst_lhs) 71921308Sache { 72021308Sache if (search_string && *search_string) 72121308Sache { 72221308Sache subst_lhs = savestring (search_string); 72321308Sache subst_lhs_len = strlen (subst_lhs); 72421308Sache } 72521308Sache else 72621308Sache { 72721308Sache subst_lhs = (char *) NULL; 72821308Sache subst_lhs_len = 0; 72921308Sache } 73021308Sache } 73121308Sache 73221308Sache FREE (subst_rhs); 73321308Sache subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len); 73421308Sache 73521308Sache /* If `&' appears in the rhs, it's supposed to be replaced 73621308Sache with the lhs. */ 73721308Sache if (member ('&', subst_rhs)) 73821308Sache postproc_subst_rhs (); 73921308Sache } 74021308Sache else 74121308Sache i += 2; 74221308Sache 74347558Sache /* If there is no lhs, the substitution can't succeed. */ 74447558Sache if (subst_lhs_len == 0) 74547558Sache { 74647558Sache *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST); 74747558Sache free (result); 74847558Sache free (temp); 74947558Sache return -1; 75047558Sache } 75147558Sache 75221308Sache l_temp = strlen (temp); 75321308Sache /* Ignore impossible cases. */ 75421308Sache if (subst_lhs_len > l_temp) 75521308Sache { 75621308Sache *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); 75721308Sache free (result); 75821308Sache free (temp); 75921308Sache return (-1); 76021308Sache } 76121308Sache 76221308Sache /* Find the first occurrence of THIS in TEMP. */ 76321308Sache si = 0; 76421308Sache for (failed = 1; (si + subst_lhs_len) <= l_temp; si++) 76521308Sache if (STREQN (temp+si, subst_lhs, subst_lhs_len)) 76621308Sache { 76721308Sache int len = subst_rhs_len - subst_lhs_len + l_temp; 768119610Sache new_event = (char *)xmalloc (1 + len); 76921308Sache strncpy (new_event, temp, si); 77021308Sache strncpy (new_event + si, subst_rhs, subst_rhs_len); 77121308Sache strncpy (new_event + si + subst_rhs_len, 77221308Sache temp + si + subst_lhs_len, 77321308Sache l_temp - (si + subst_lhs_len)); 77421308Sache new_event[len] = '\0'; 77521308Sache free (temp); 77621308Sache temp = new_event; 77721308Sache 77821308Sache failed = 0; 77921308Sache 78021308Sache if (substitute_globally) 78121308Sache { 78221308Sache si += subst_rhs_len; 78321308Sache l_temp = strlen (temp); 78421308Sache substitute_globally++; 78521308Sache continue; 78621308Sache } 78721308Sache else 78821308Sache break; 78921308Sache } 79021308Sache 79121308Sache if (substitute_globally > 1) 79221308Sache { 79321308Sache substitute_globally = 0; 79421308Sache continue; /* don't want to increment i */ 79521308Sache } 79621308Sache 79721308Sache if (failed == 0) 79821308Sache continue; /* don't want to increment i */ 79921308Sache 80021308Sache *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); 80121308Sache free (result); 80221308Sache free (temp); 80321308Sache return (-1); 80421308Sache } 80521308Sache } 80621308Sache i += 2; 80721308Sache } 80821308Sache /* Done with modfiers. */ 80921308Sache /* Believe it or not, we have to back the pointer up by one. */ 81021308Sache --i; 81121308Sache 81221308Sache if (want_quotes) 81321308Sache { 81421308Sache char *x; 81521308Sache 81621308Sache if (want_quotes == 'q') 81775406Sache x = sh_single_quote (temp); 81821308Sache else if (want_quotes == 'x') 81921308Sache x = quote_breaks (temp); 82021308Sache else 82121308Sache x = savestring (temp); 82221308Sache 82321308Sache free (temp); 82421308Sache temp = x; 82521308Sache } 82621308Sache 82721308Sache n = strlen (temp); 82821308Sache if (n >= result_len) 829119610Sache result = (char *)xrealloc (result, n + 2); 83021308Sache strcpy (result, temp); 83121308Sache free (temp); 83221308Sache 83321308Sache *end_index_ptr = i; 83421308Sache *ret_string = result; 83521308Sache return (print_only); 83621308Sache} 83721308Sache 83821308Sache/* Expand the string STRING, placing the result into OUTPUT, a pointer 83921308Sache to a string. Returns: 84021308Sache 84121308Sache -1) If there was an error in expansion. 84221308Sache 0) If no expansions took place (or, if the only change in 84321308Sache the text was the de-slashifying of the history expansion 84421308Sache character) 84521308Sache 1) If expansions did take place 84621308Sache 2) If the `p' modifier was given and the caller should print the result 84721308Sache 84821308Sache If an error ocurred in expansion, then OUTPUT contains a descriptive 84921308Sache error message. */ 85021308Sache 85121308Sache#define ADD_STRING(s) \ 85221308Sache do \ 85321308Sache { \ 85421308Sache int sl = strlen (s); \ 85521308Sache j += sl; \ 85621308Sache if (j >= result_len) \ 85721308Sache { \ 85821308Sache while (j >= result_len) \ 85921308Sache result_len += 128; \ 860119610Sache result = (char *)xrealloc (result, result_len); \ 86121308Sache } \ 86221308Sache strcpy (result + j - sl, s); \ 86321308Sache } \ 86421308Sache while (0) 86521308Sache 86621308Sache#define ADD_CHAR(c) \ 86721308Sache do \ 86821308Sache { \ 86921308Sache if (j >= result_len - 1) \ 870119610Sache result = (char *)xrealloc (result, result_len += 64); \ 87121308Sache result[j++] = c; \ 87221308Sache result[j] = '\0'; \ 87321308Sache } \ 87421308Sache while (0) 87521308Sache 87621308Sacheint 87721308Sachehistory_expand (hstring, output) 87821308Sache char *hstring; 87921308Sache char **output; 88021308Sache{ 88121308Sache register int j; 88221308Sache int i, r, l, passc, cc, modified, eindex, only_printing; 88321308Sache char *string; 88421308Sache 88521308Sache /* The output string, and its length. */ 88621308Sache int result_len; 88721308Sache char *result; 88821308Sache 889119610Sache#if defined (HANDLE_MULTIBYTE) 890119610Sache char mb[MB_LEN_MAX]; 891119610Sache mbstate_t ps; 892119610Sache#endif 893119610Sache 89421308Sache /* Used when adding the string. */ 89521308Sache char *temp; 89621308Sache 89775406Sache if (output == 0) 89875406Sache return 0; 89975406Sache 90021308Sache /* Setting the history expansion character to 0 inhibits all 90121308Sache history expansion. */ 90221308Sache if (history_expansion_char == 0) 90321308Sache { 90421308Sache *output = savestring (hstring); 90521308Sache return (0); 90621308Sache } 90721308Sache 90821308Sache /* Prepare the buffer for printing error messages. */ 909119610Sache result = (char *)xmalloc (result_len = 256); 91021308Sache result[0] = '\0'; 91121308Sache 91221308Sache only_printing = modified = 0; 91321308Sache l = strlen (hstring); 91421308Sache 91535486Sache /* Grovel the string. Only backslash and single quotes can quote the 91635486Sache history escape character. We also handle arg specifiers. */ 91721308Sache 91821308Sache /* Before we grovel forever, see if the history_expansion_char appears 91921308Sache anywhere within the text. */ 92021308Sache 92121308Sache /* The quick substitution character is a history expansion all right. That 92221308Sache is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact, 92321308Sache that is the substitution that we do. */ 92421308Sache if (hstring[0] == history_subst_char) 92521308Sache { 926119610Sache string = (char *)xmalloc (l + 5); 92721308Sache 92821308Sache string[0] = string[1] = history_expansion_char; 92921308Sache string[2] = ':'; 93021308Sache string[3] = 's'; 93121308Sache strcpy (string + 4, hstring); 93221308Sache l += 4; 93321308Sache } 93421308Sache else 93521308Sache { 936119610Sache#if defined (HANDLE_MULTIBYTE) 937119610Sache memset (&ps, 0, sizeof (mbstate_t)); 938119610Sache#endif 939119610Sache 94021308Sache string = hstring; 94121308Sache /* If not quick substitution, still maybe have to do expansion. */ 94221308Sache 94321308Sache /* `!' followed by one of the characters in history_no_expand_chars 94421308Sache is NOT an expansion. */ 94521308Sache for (i = 0; string[i]; i++) 94621308Sache { 947119610Sache#if defined (HANDLE_MULTIBYTE) 948119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 949119610Sache { 950119610Sache int v; 951119610Sache v = _rl_get_char_len (string + i, &ps); 952119610Sache if (v > 1) 953119610Sache { 954119610Sache i += v - 1; 955119610Sache continue; 956119610Sache } 957119610Sache } 958119610Sache#endif /* HANDLE_MULTIBYTE */ 959119610Sache 96021308Sache cc = string[i + 1]; 961119610Sache /* The history_comment_char, if set, appearing at the beginning 96235486Sache of a word signifies that the rest of the line should not have 96335486Sache history expansion performed on it. 96435486Sache Skip the rest of the line and break out of the loop. */ 96535486Sache if (history_comment_char && string[i] == history_comment_char && 96675406Sache (i == 0 || member (string[i - 1], history_word_delimiters))) 96721308Sache { 96835486Sache while (string[i]) 96935486Sache i++; 97035486Sache break; 97135486Sache } 97235486Sache else if (string[i] == history_expansion_char) 97335486Sache { 97421308Sache if (!cc || member (cc, history_no_expand_chars)) 97521308Sache continue; 97626497Sache /* If the calling application has set 97726497Sache history_inhibit_expansion_function to a function that checks 97826497Sache for special cases that should not be history expanded, 97926497Sache call the function and skip the expansion if it returns a 98026497Sache non-zero value. */ 98126497Sache else if (history_inhibit_expansion_function && 98226497Sache (*history_inhibit_expansion_function) (string, i)) 98321308Sache continue; 98421308Sache else 98521308Sache break; 98621308Sache } 98735486Sache /* XXX - at some point, might want to extend this to handle 98835486Sache double quotes as well. */ 98921308Sache else if (history_quotes_inhibit_expansion && string[i] == '\'') 99021308Sache { 99121308Sache /* If this is bash, single quotes inhibit history expansion. */ 99221308Sache i++; 99321308Sache hist_string_extract_single_quoted (string, &i); 99421308Sache } 99521308Sache else if (history_quotes_inhibit_expansion && string[i] == '\\') 99621308Sache { 99721308Sache /* If this is bash, allow backslashes to quote single 99821308Sache quotes and the history expansion character. */ 99921308Sache if (cc == '\'' || cc == history_expansion_char) 100021308Sache i++; 100121308Sache } 100221308Sache } 100321308Sache 100421308Sache if (string[i] != history_expansion_char) 100521308Sache { 100621308Sache free (result); 100721308Sache *output = savestring (string); 100821308Sache return (0); 100921308Sache } 101021308Sache } 101121308Sache 101221308Sache /* Extract and perform the substitution. */ 101321308Sache for (passc = i = j = 0; i < l; i++) 101421308Sache { 101521308Sache int tchar = string[i]; 101621308Sache 101721308Sache if (passc) 101821308Sache { 101921308Sache passc = 0; 102021308Sache ADD_CHAR (tchar); 102121308Sache continue; 102221308Sache } 102321308Sache 1024119610Sache#if defined (HANDLE_MULTIBYTE) 1025119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 1026119610Sache { 1027119610Sache int k, c; 1028119610Sache 1029119610Sache c = tchar; 1030119610Sache memset (mb, 0, sizeof (mb)); 1031119610Sache for (k = 0; k < MB_LEN_MAX; k++) 1032119610Sache { 1033119610Sache mb[k] = (char)c; 1034119610Sache memset (&ps, 0, sizeof (mbstate_t)); 1035119610Sache if (_rl_get_char_len (mb, &ps) == -2) 1036119610Sache c = string[++i]; 1037119610Sache else 1038119610Sache break; 1039119610Sache } 1040119610Sache if (strlen (mb) > 1) 1041119610Sache { 1042119610Sache ADD_STRING (mb); 1043119610Sache break; 1044119610Sache } 1045119610Sache } 1046119610Sache#endif /* HANDLE_MULTIBYTE */ 1047119610Sache 104821308Sache if (tchar == history_expansion_char) 104921308Sache tchar = -3; 105035486Sache else if (tchar == history_comment_char) 105135486Sache tchar = -2; 105221308Sache 105321308Sache switch (tchar) 105421308Sache { 105521308Sache default: 105621308Sache ADD_CHAR (string[i]); 105721308Sache break; 105821308Sache 105921308Sache case '\\': 106021308Sache passc++; 106121308Sache ADD_CHAR (tchar); 106221308Sache break; 106321308Sache 106421308Sache case '\'': 106521308Sache { 106621308Sache /* If history_quotes_inhibit_expansion is set, single quotes 106721308Sache inhibit history expansion. */ 106821308Sache if (history_quotes_inhibit_expansion) 106921308Sache { 107021308Sache int quote, slen; 107121308Sache 107221308Sache quote = i++; 107321308Sache hist_string_extract_single_quoted (string, &i); 107421308Sache 107521308Sache slen = i - quote + 2; 1076119610Sache temp = (char *)xmalloc (slen); 107721308Sache strncpy (temp, string + quote, slen); 107821308Sache temp[slen - 1] = '\0'; 107921308Sache ADD_STRING (temp); 108021308Sache free (temp); 108121308Sache } 108221308Sache else 108321308Sache ADD_CHAR (string[i]); 108421308Sache break; 108521308Sache } 108621308Sache 108735486Sache case -2: /* history_comment_char */ 108875406Sache if (i == 0 || member (string[i - 1], history_word_delimiters)) 108935486Sache { 1090119610Sache temp = (char *)xmalloc (l - i + 1); 109135486Sache strcpy (temp, string + i); 109235486Sache ADD_STRING (temp); 109335486Sache free (temp); 109435486Sache i = l; 109535486Sache } 109635486Sache else 109735486Sache ADD_CHAR (string[i]); 109835486Sache break; 109935486Sache 110021308Sache case -3: /* history_expansion_char */ 110121308Sache cc = string[i + 1]; 110221308Sache 110321308Sache /* If the history_expansion_char is followed by one of the 110421308Sache characters in history_no_expand_chars, then it is not a 110521308Sache candidate for expansion of any kind. */ 110621308Sache if (member (cc, history_no_expand_chars)) 110721308Sache { 110821308Sache ADD_CHAR (string[i]); 110921308Sache break; 111021308Sache } 111121308Sache 111221308Sache#if defined (NO_BANG_HASH_MODIFIERS) 111321308Sache /* There is something that is listed as a `word specifier' in csh 111421308Sache documentation which means `the expanded text to this point'. 111521308Sache That is not a word specifier, it is an event specifier. If we 111621308Sache don't want to allow modifiers with `!#', just stick the current 111721308Sache output line in again. */ 111821308Sache if (cc == '#') 111921308Sache { 112021308Sache if (result) 112121308Sache { 1122119610Sache temp = (char *)xmalloc (1 + strlen (result)); 112321308Sache strcpy (temp, result); 112421308Sache ADD_STRING (temp); 112521308Sache free (temp); 112621308Sache } 112721308Sache i++; 112821308Sache break; 112921308Sache } 113021308Sache#endif 113121308Sache 113221308Sache r = history_expand_internal (string, i, &eindex, &temp, result); 113321308Sache if (r < 0) 113421308Sache { 113521308Sache *output = temp; 113621308Sache free (result); 113721308Sache if (string != hstring) 113821308Sache free (string); 113921308Sache return -1; 114021308Sache } 114121308Sache else 114221308Sache { 114321308Sache if (temp) 114421308Sache { 114521308Sache modified++; 114621308Sache if (*temp) 114721308Sache ADD_STRING (temp); 114821308Sache free (temp); 114921308Sache } 115021308Sache only_printing = r == 1; 115121308Sache i = eindex; 115221308Sache } 115321308Sache break; 115421308Sache } 115521308Sache } 115621308Sache 115721308Sache *output = result; 115821308Sache if (string != hstring) 115921308Sache free (string); 116021308Sache 116121308Sache if (only_printing) 116221308Sache { 116321308Sache add_history (result); 116421308Sache return (2); 116521308Sache } 116621308Sache 116721308Sache return (modified != 0); 116821308Sache} 116921308Sache 117021308Sache/* Return a consed string which is the word specified in SPEC, and found 117121308Sache in FROM. NULL is returned if there is no spec. The address of 117221308Sache ERROR_POINTER is returned if the word specified cannot be found. 117321308Sache CALLER_INDEX is the offset in SPEC to start looking; it is updated 117421308Sache to point to just after the last character parsed. */ 117521308Sachestatic char * 117621308Sacheget_history_word_specifier (spec, from, caller_index) 117721308Sache char *spec, *from; 117821308Sache int *caller_index; 117921308Sache{ 118021308Sache register int i = *caller_index; 118121308Sache int first, last; 118221308Sache int expecting_word_spec = 0; 118321308Sache char *result; 118421308Sache 118521308Sache /* The range of words to return doesn't exist yet. */ 118621308Sache first = last = 0; 118721308Sache result = (char *)NULL; 118821308Sache 118921308Sache /* If we found a colon, then this *must* be a word specification. If 119021308Sache it isn't, then it is an error. */ 119121308Sache if (spec[i] == ':') 119221308Sache { 119321308Sache i++; 119421308Sache expecting_word_spec++; 119521308Sache } 119621308Sache 119721308Sache /* Handle special cases first. */ 119821308Sache 119921308Sache /* `%' is the word last searched for. */ 120021308Sache if (spec[i] == '%') 120121308Sache { 120221308Sache *caller_index = i + 1; 120321308Sache return (search_match ? savestring (search_match) : savestring ("")); 120421308Sache } 120521308Sache 120621308Sache /* `*' matches all of the arguments, but not the command. */ 120721308Sache if (spec[i] == '*') 120821308Sache { 120921308Sache *caller_index = i + 1; 121021308Sache result = history_arg_extract (1, '$', from); 121121308Sache return (result ? result : savestring ("")); 121221308Sache } 121321308Sache 121421308Sache /* `$' is last arg. */ 121521308Sache if (spec[i] == '$') 121621308Sache { 121721308Sache *caller_index = i + 1; 121821308Sache return (history_arg_extract ('$', '$', from)); 121921308Sache } 122021308Sache 122121308Sache /* Try to get FIRST and LAST figured out. */ 122221308Sache 122321308Sache if (spec[i] == '-') 122421308Sache first = 0; 122521308Sache else if (spec[i] == '^') 122621308Sache first = 1; 122721308Sache else if (_rl_digit_p (spec[i]) && expecting_word_spec) 122821308Sache { 122921308Sache for (first = 0; _rl_digit_p (spec[i]); i++) 123021308Sache first = (first * 10) + _rl_digit_value (spec[i]); 123121308Sache } 123221308Sache else 123321308Sache return ((char *)NULL); /* no valid `first' for word specifier */ 123421308Sache 123521308Sache if (spec[i] == '^' || spec[i] == '*') 123621308Sache { 123721308Sache last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */ 123821308Sache i++; 123921308Sache } 124021308Sache else if (spec[i] != '-') 124121308Sache last = first; 124221308Sache else 124321308Sache { 124421308Sache i++; 124521308Sache 124621308Sache if (_rl_digit_p (spec[i])) 124721308Sache { 124821308Sache for (last = 0; _rl_digit_p (spec[i]); i++) 124921308Sache last = (last * 10) + _rl_digit_value (spec[i]); 125021308Sache } 125121308Sache else if (spec[i] == '$') 125221308Sache { 125321308Sache i++; 125421308Sache last = '$'; 125521308Sache } 1256119610Sache#if 0 1257119610Sache else if (!spec[i] || spec[i] == ':') 1258119610Sache /* check against `:' because there could be a modifier separator */ 1259119610Sache#else 1260119610Sache else 1261119610Sache /* csh seems to allow anything to terminate the word spec here, 1262119610Sache leaving it as an abbreviation. */ 1263119610Sache#endif 126421308Sache last = -1; /* x- abbreviates x-$ omitting word `$' */ 126521308Sache } 126621308Sache 126721308Sache *caller_index = i; 126821308Sache 126921308Sache if (last >= first || last == '$' || last < 0) 127021308Sache result = history_arg_extract (first, last, from); 127121308Sache 127221308Sache return (result ? result : (char *)&error_pointer); 127321308Sache} 127421308Sache 127521308Sache/* Extract the args specified, starting at FIRST, and ending at LAST. 127621308Sache The args are taken from STRING. If either FIRST or LAST is < 0, 127721308Sache then make that arg count from the right (subtract from the number of 127821308Sache tokens, so that FIRST = -1 means the next to last token on the line). 127921308Sache If LAST is `$' the last arg from STRING is used. */ 128021308Sachechar * 128121308Sachehistory_arg_extract (first, last, string) 128221308Sache int first, last; 128375406Sache const char *string; 128421308Sache{ 128521308Sache register int i, len; 128621308Sache char *result; 128721308Sache int size, offset; 128821308Sache char **list; 128921308Sache 129021308Sache /* XXX - think about making history_tokenize return a struct array, 129121308Sache each struct in array being a string and a length to avoid the 129221308Sache calls to strlen below. */ 129321308Sache if ((list = history_tokenize (string)) == NULL) 129421308Sache return ((char *)NULL); 129521308Sache 129621308Sache for (len = 0; list[len]; len++) 129721308Sache ; 129821308Sache 129921308Sache if (last < 0) 130021308Sache last = len + last - 1; 130121308Sache 130221308Sache if (first < 0) 130321308Sache first = len + first - 1; 130421308Sache 130521308Sache if (last == '$') 130621308Sache last = len - 1; 130721308Sache 130821308Sache if (first == '$') 130921308Sache first = len - 1; 131021308Sache 131121308Sache last++; 131221308Sache 131321308Sache if (first >= len || last > len || first < 0 || last < 0 || first > last) 131421308Sache result = ((char *)NULL); 131521308Sache else 131621308Sache { 131721308Sache for (size = 0, i = first; i < last; i++) 131821308Sache size += strlen (list[i]) + 1; 1319119610Sache result = (char *)xmalloc (size + 1); 132021308Sache result[0] = '\0'; 132121308Sache 132221308Sache for (i = first, offset = 0; i < last; i++) 132321308Sache { 132421308Sache strcpy (result + offset, list[i]); 132521308Sache offset += strlen (list[i]); 132621308Sache if (i + 1 < last) 132721308Sache { 132821308Sache result[offset++] = ' '; 132921308Sache result[offset] = 0; 133021308Sache } 133121308Sache } 133221308Sache } 133321308Sache 133421308Sache for (i = 0; i < len; i++) 133521308Sache free (list[i]); 133621308Sache free (list); 133721308Sache 133821308Sache return (result); 133921308Sache} 134021308Sache 134121308Sache#define slashify_in_quotes "\\`\"$" 134221308Sache 134321308Sache/* Parse STRING into tokens and return an array of strings. If WIND is 134421308Sache not -1 and INDP is not null, we also want the word surrounding index 134521308Sache WIND. The position in the returned array of strings is returned in 134621308Sache *INDP. */ 134721308Sachestatic char ** 134821308Sachehistory_tokenize_internal (string, wind, indp) 134975406Sache const char *string; 135021308Sache int wind, *indp; 135121308Sache{ 135221308Sache char **result; 135321308Sache register int i, start, result_index, size; 135421308Sache int len, delimiter; 135521308Sache 135675406Sache /* If we're searching for a string that's not part of a word (e.g., " "), 135775406Sache make sure we set *INDP to a reasonable value. */ 135875406Sache if (indp && wind != -1) 135975406Sache *indp = -1; 136075406Sache 136121308Sache /* Get a token, and stuff it into RESULT. The tokens are split 136221308Sache exactly where the shell would split them. */ 136321308Sache for (i = result_index = size = 0, result = (char **)NULL; string[i]; ) 136421308Sache { 136521308Sache delimiter = 0; 136621308Sache 136721308Sache /* Skip leading whitespace. */ 136821308Sache for (; string[i] && whitespace (string[i]); i++) 136921308Sache ; 137021308Sache if (string[i] == 0 || string[i] == history_comment_char) 137121308Sache return (result); 137221308Sache 137321308Sache start = i; 137421308Sache 137521308Sache if (member (string[i], "()\n")) 137621308Sache { 137721308Sache i++; 137821308Sache goto got_token; 137921308Sache } 138021308Sache 138121308Sache if (member (string[i], "<>;&|$")) 138221308Sache { 138321308Sache int peek = string[i + 1]; 138421308Sache 138521308Sache if (peek == string[i] && peek != '$') 138621308Sache { 138721308Sache if (peek == '<' && string[i + 2] == '-') 138821308Sache i++; 138921308Sache i += 2; 139021308Sache goto got_token; 139121308Sache } 139221308Sache else 139321308Sache { 139421308Sache if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || 139521308Sache ((peek == '>') && (string[i] == '&')) || 139621308Sache ((peek == '(') && (string[i] == '$'))) 139721308Sache { 139821308Sache i += 2; 139921308Sache goto got_token; 140021308Sache } 140121308Sache } 140221308Sache if (string[i] != '$') 140321308Sache { 140421308Sache i++; 140521308Sache goto got_token; 140621308Sache } 140721308Sache } 140821308Sache 140921308Sache /* Get word from string + i; */ 141021308Sache 141135486Sache if (member (string[i], HISTORY_QUOTE_CHARACTERS)) 141221308Sache delimiter = string[i++]; 141321308Sache 141421308Sache for (; string[i]; i++) 141521308Sache { 141621308Sache if (string[i] == '\\' && string[i + 1] == '\n') 141721308Sache { 141821308Sache i++; 141921308Sache continue; 142021308Sache } 142121308Sache 142221308Sache if (string[i] == '\\' && delimiter != '\'' && 142321308Sache (delimiter != '"' || member (string[i], slashify_in_quotes))) 142421308Sache { 142521308Sache i++; 142621308Sache continue; 142721308Sache } 142821308Sache 142921308Sache if (delimiter && string[i] == delimiter) 143021308Sache { 143121308Sache delimiter = 0; 143221308Sache continue; 143321308Sache } 143421308Sache 143575406Sache if (!delimiter && (member (string[i], history_word_delimiters))) 143621308Sache break; 143721308Sache 143835486Sache if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)) 143921308Sache delimiter = string[i]; 144021308Sache } 144121308Sache 144221308Sache got_token: 144321308Sache 144421308Sache /* If we are looking for the word in which the character at a 144521308Sache particular index falls, remember it. */ 144621308Sache if (indp && wind != -1 && wind >= start && wind < i) 144721308Sache *indp = result_index; 144821308Sache 144921308Sache len = i - start; 145021308Sache if (result_index + 2 >= size) 145121308Sache result = (char **)xrealloc (result, ((size += 10) * sizeof (char *))); 1452119610Sache result[result_index] = (char *)xmalloc (1 + len); 145321308Sache strncpy (result[result_index], string + start, len); 145421308Sache result[result_index][len] = '\0'; 145521308Sache result[++result_index] = (char *)NULL; 145621308Sache } 145721308Sache 145821308Sache return (result); 145921308Sache} 146021308Sache 146121308Sache/* Return an array of tokens, much as the shell might. The tokens are 146221308Sache parsed out of STRING. */ 146321308Sachechar ** 146421308Sachehistory_tokenize (string) 146575406Sache const char *string; 146621308Sache{ 146721308Sache return (history_tokenize_internal (string, -1, (int *)NULL)); 146821308Sache} 146921308Sache 147021308Sache/* Find and return the word which contains the character at index IND 147121308Sache in the history line LINE. Used to save the word matched by the 147221308Sache last history !?string? search. */ 147321308Sachestatic char * 147421308Sachehistory_find_word (line, ind) 147521308Sache char *line; 147621308Sache int ind; 147721308Sache{ 147821308Sache char **words, *s; 147921308Sache int i, wind; 148021308Sache 148121308Sache words = history_tokenize_internal (line, ind, &wind); 148275406Sache if (wind == -1 || words == 0) 148321308Sache return ((char *)NULL); 148421308Sache s = words[wind]; 148521308Sache for (i = 0; i < wind; i++) 148621308Sache free (words[i]); 148721308Sache for (i = wind + 1; words[i]; i++) 148821308Sache free (words[i]); 148921308Sache free (words); 149021308Sache return s; 149121308Sache} 1492