121308Sache/* histexpand.c -- history expansion. */ 221308Sache 3136644Sache/* Copyright (C) 1989-2004 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 55136644Sache#define slashify_in_quotes "\\`\"$" 56136644Sache 57119610Sachetypedef int _hist_search_func_t PARAMS((const char *, int)); 5875406Sache 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)); 68136644Sachestatic int history_tokenize_word PARAMS((const char *, int)); 69136644Sachestatic char *history_substring PARAMS((const char *, int, int)); 7021308Sache 71119610Sachestatic char *quote_breaks PARAMS((char *)); 7221308Sache 7321308Sache/* Variables exported by this file. */ 7421308Sache/* The character that represents the start of a history expansion 7521308Sache request. This is usually `!'. */ 7621308Sachechar history_expansion_char = '!'; 7721308Sache 7821308Sache/* The character that invokes word substitution if found at the start of 7921308Sache a line. This is usually `^'. */ 8021308Sachechar history_subst_char = '^'; 8121308Sache 8221308Sache/* During tokenization, if this character is seen as the first character 8321308Sache of a word, then it, and all subsequent characters upto a newline are 8421308Sache ignored. For a Bourne shell, this should be '#'. Bash special cases 8521308Sache the interactive comment character to not be a comment delimiter. */ 8621308Sachechar history_comment_char = '\0'; 8721308Sache 8821308Sache/* The list of characters which inhibit the expansion of text if found 8921308Sache immediately following history_expansion_char. */ 9021308Sachechar *history_no_expand_chars = " \t\n\r="; 9121308Sache 9221308Sache/* If set to a non-zero value, single quotes inhibit history expansion. 9321308Sache The default is 0. */ 9421308Sacheint history_quotes_inhibit_expansion = 0; 9521308Sache 9675406Sache/* Used to split words by history_tokenize_internal. */ 9775406Sachechar *history_word_delimiters = HISTORY_WORD_DELIMITERS; 9875406Sache 9926497Sache/* If set, this points to a function that is called to verify that a 10026497Sache particular history expansion should be performed. */ 10175406Sacherl_linebuf_func_t *history_inhibit_expansion_function; 10226497Sache 10321308Sache/* **************************************************************** */ 10421308Sache/* */ 10521308Sache/* History Expansion */ 10621308Sache/* */ 10721308Sache/* **************************************************************** */ 10821308Sache 10921308Sache/* Hairy history expansion on text, not tokens. This is of general 11021308Sache use, and thus belongs in this library. */ 11121308Sache 11221308Sache/* The last string searched for by a !?string? search. */ 11321308Sachestatic char *search_string; 11421308Sache 11521308Sache/* The last string matched by a !?string? search. */ 11621308Sachestatic char *search_match; 11721308Sache 11821308Sache/* Return the event specified at TEXT + OFFSET modifying OFFSET to 11921308Sache point to after the event specifier. Just a pointer to the history 12021308Sache line is returned; NULL is returned in the event of a bad specifier. 12121308Sache You pass STRING with *INDEX equal to the history_expansion_char that 12221308Sache begins this specification. 12321308Sache DELIMITING_QUOTE is a character that is allowed to end the string 12421308Sache specification for what to search for in addition to the normal 12521308Sache characters `:', ` ', `\t', `\n', and sometimes `?'. 12621308Sache So you might call this function like: 12721308Sache line = get_history_event ("!echo:p", &index, 0); */ 12821308Sachechar * 12921308Sacheget_history_event (string, caller_index, delimiting_quote) 13075406Sache const char *string; 13121308Sache int *caller_index; 13221308Sache int delimiting_quote; 13321308Sache{ 13421308Sache register int i; 13521308Sache register char c; 13621308Sache HIST_ENTRY *entry; 13721308Sache int which, sign, local_index, substring_okay; 13875406Sache _hist_search_func_t *search_func; 13921308Sache char *temp; 14021308Sache 14121308Sache /* The event can be specified in a number of ways. 14221308Sache 14321308Sache !! the previous command 14421308Sache !n command line N 14521308Sache !-n current command-line minus N 14621308Sache !str the most recent command starting with STR 14721308Sache !?str[?] 14821308Sache the most recent command containing STR 14921308Sache 15021308Sache All values N are determined via HISTORY_BASE. */ 15121308Sache 15221308Sache i = *caller_index; 15321308Sache 15421308Sache if (string[i] != history_expansion_char) 15521308Sache return ((char *)NULL); 15621308Sache 15721308Sache /* Move on to the specification. */ 15821308Sache i++; 15921308Sache 16021308Sache sign = 1; 16121308Sache substring_okay = 0; 16221308Sache 16321308Sache#define RETURN_ENTRY(e, w) \ 16421308Sache return ((e = history_get (w)) ? e->line : (char *)NULL) 16521308Sache 16621308Sache /* Handle !! case. */ 16721308Sache if (string[i] == history_expansion_char) 16821308Sache { 16921308Sache i++; 17021308Sache which = history_base + (history_length - 1); 17121308Sache *caller_index = i; 17221308Sache RETURN_ENTRY (entry, which); 17321308Sache } 17421308Sache 17521308Sache /* Hack case of numeric line specification. */ 17621308Sache if (string[i] == '-') 17721308Sache { 17821308Sache sign = -1; 17921308Sache i++; 18021308Sache } 18121308Sache 18221308Sache if (_rl_digit_p (string[i])) 18321308Sache { 18421308Sache /* Get the extent of the digits and compute the value. */ 18521308Sache for (which = 0; _rl_digit_p (string[i]); i++) 18621308Sache which = (which * 10) + _rl_digit_value (string[i]); 18721308Sache 18821308Sache *caller_index = i; 18921308Sache 19021308Sache if (sign < 0) 19121308Sache which = (history_length + history_base) - which; 19221308Sache 19321308Sache RETURN_ENTRY (entry, which); 19421308Sache } 19521308Sache 19621308Sache /* This must be something to search for. If the spec begins with 19721308Sache a '?', then the string may be anywhere on the line. Otherwise, 19821308Sache the string must be found at the start of a line. */ 19921308Sache if (string[i] == '?') 20021308Sache { 20121308Sache substring_okay++; 20221308Sache i++; 20321308Sache } 20421308Sache 20521308Sache /* Only a closing `?' or a newline delimit a substring search string. */ 20621308Sache for (local_index = i; c = string[i]; i++) 207157184Sache { 208119610Sache#if defined (HANDLE_MULTIBYTE) 209157184Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 210157184Sache { 211157184Sache int v; 212157184Sache mbstate_t ps; 21321308Sache 214157184Sache memset (&ps, 0, sizeof (mbstate_t)); 215157184Sache /* These produce warnings because we're passing a const string to a 216157184Sache function that takes a non-const string. */ 217157184Sache _rl_adjust_point ((char *)string, i, &ps); 218157184Sache if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1) 219157184Sache { 220157184Sache i += v - 1; 221157184Sache continue; 222157184Sache } 223157184Sache } 224157184Sache 225119610Sache#endif /* HANDLE_MULTIBYTE */ 226119610Sache if ((!substring_okay && (whitespace (c) || c == ':' || 227119610Sache (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) || 228119610Sache string[i] == delimiting_quote)) || 229119610Sache string[i] == '\n' || 230119610Sache (substring_okay && string[i] == '?')) 231119610Sache break; 232157184Sache } 233119610Sache 23421308Sache which = i - local_index; 235119610Sache temp = (char *)xmalloc (1 + which); 23621308Sache if (which) 23721308Sache strncpy (temp, string + local_index, which); 23821308Sache temp[which] = '\0'; 23921308Sache 24021308Sache if (substring_okay && string[i] == '?') 24121308Sache i++; 24221308Sache 24321308Sache *caller_index = i; 24421308Sache 24521308Sache#define FAIL_SEARCH() \ 24621308Sache do { \ 24721308Sache history_offset = history_length; free (temp) ; return (char *)NULL; \ 24821308Sache } while (0) 24921308Sache 25021308Sache /* If there is no search string, try to use the previous search string, 25121308Sache if one exists. If not, fail immediately. */ 25221308Sache if (*temp == '\0' && substring_okay) 25321308Sache { 25421308Sache if (search_string) 25521308Sache { 25621308Sache free (temp); 25721308Sache temp = savestring (search_string); 25821308Sache } 25921308Sache else 26021308Sache FAIL_SEARCH (); 26121308Sache } 26221308Sache 26321308Sache search_func = substring_okay ? history_search : history_search_prefix; 26421308Sache while (1) 26521308Sache { 26621308Sache local_index = (*search_func) (temp, -1); 26721308Sache 26821308Sache if (local_index < 0) 26921308Sache FAIL_SEARCH (); 27021308Sache 27121308Sache if (local_index == 0 || substring_okay) 27221308Sache { 27321308Sache entry = current_history (); 27421308Sache history_offset = history_length; 27521308Sache 27621308Sache /* If this was a substring search, then remember the 27721308Sache string that we matched for word substitution. */ 27821308Sache if (substring_okay) 27921308Sache { 28021308Sache FREE (search_string); 28121308Sache search_string = temp; 28221308Sache 28321308Sache FREE (search_match); 28421308Sache search_match = history_find_word (entry->line, local_index); 28521308Sache } 28621308Sache else 28721308Sache free (temp); 28821308Sache 28921308Sache return (entry->line); 29021308Sache } 29121308Sache 29221308Sache if (history_offset) 29321308Sache history_offset--; 29421308Sache else 29521308Sache FAIL_SEARCH (); 29621308Sache } 29721308Sache#undef FAIL_SEARCH 29821308Sache#undef RETURN_ENTRY 29921308Sache} 30021308Sache 30121308Sache/* Function for extracting single-quoted strings. Used for inhibiting 30221308Sache history expansion within single quotes. */ 30321308Sache 30421308Sache/* Extract the contents of STRING as if it is enclosed in single quotes. 30521308Sache SINDEX, when passed in, is the offset of the character immediately 30621308Sache following the opening single quote; on exit, SINDEX is left pointing 30721308Sache to the closing single quote. */ 30821308Sachestatic void 30921308Sachehist_string_extract_single_quoted (string, sindex) 31021308Sache char *string; 31121308Sache int *sindex; 31221308Sache{ 31321308Sache register int i; 31421308Sache 31521308Sache for (i = *sindex; string[i] && string[i] != '\''; i++) 31621308Sache ; 31721308Sache 31821308Sache *sindex = i; 31921308Sache} 32021308Sache 32121308Sachestatic char * 32221308Sachequote_breaks (s) 32321308Sache char *s; 32421308Sache{ 32521308Sache register char *p, *r; 32621308Sache char *ret; 32721308Sache int len = 3; 32821308Sache 32921308Sache for (p = s; p && *p; p++, len++) 33021308Sache { 33121308Sache if (*p == '\'') 33221308Sache len += 3; 33321308Sache else if (whitespace (*p) || *p == '\n') 33421308Sache len += 2; 33521308Sache } 33621308Sache 337119610Sache r = ret = (char *)xmalloc (len); 33821308Sache *r++ = '\''; 33921308Sache for (p = s; p && *p; ) 34021308Sache { 34121308Sache if (*p == '\'') 34221308Sache { 34321308Sache *r++ = '\''; 34421308Sache *r++ = '\\'; 34521308Sache *r++ = '\''; 34621308Sache *r++ = '\''; 34721308Sache p++; 34821308Sache } 34921308Sache else if (whitespace (*p) || *p == '\n') 35021308Sache { 35121308Sache *r++ = '\''; 35221308Sache *r++ = *p++; 35321308Sache *r++ = '\''; 35421308Sache } 35521308Sache else 35621308Sache *r++ = *p++; 35721308Sache } 35821308Sache *r++ = '\''; 35921308Sache *r = '\0'; 36021308Sache return ret; 36121308Sache} 36221308Sache 36321308Sachestatic char * 36421308Sachehist_error(s, start, current, errtype) 36521308Sache char *s; 36621308Sache int start, current, errtype; 36721308Sache{ 36875406Sache char *temp; 36975406Sache const char *emsg; 37021308Sache int ll, elen; 37121308Sache 37221308Sache ll = current - start; 37321308Sache 37421308Sache switch (errtype) 37521308Sache { 37621308Sache case EVENT_NOT_FOUND: 37721308Sache emsg = "event not found"; 37821308Sache elen = 15; 37921308Sache break; 38021308Sache case BAD_WORD_SPEC: 38121308Sache emsg = "bad word specifier"; 38221308Sache elen = 18; 38321308Sache break; 38421308Sache case SUBST_FAILED: 38521308Sache emsg = "substitution failed"; 38621308Sache elen = 19; 38721308Sache break; 38821308Sache case BAD_MODIFIER: 38921308Sache emsg = "unrecognized history modifier"; 39021308Sache elen = 29; 39121308Sache break; 39247558Sache case NO_PREV_SUBST: 39347558Sache emsg = "no previous substitution"; 39447558Sache elen = 24; 39547558Sache break; 39621308Sache default: 39721308Sache emsg = "unknown expansion error"; 39821308Sache elen = 23; 39921308Sache break; 40021308Sache } 40121308Sache 402119610Sache temp = (char *)xmalloc (ll + elen + 3); 40321308Sache strncpy (temp, s + start, ll); 40421308Sache temp[ll] = ':'; 40521308Sache temp[ll + 1] = ' '; 40621308Sache strcpy (temp + ll + 2, emsg); 40721308Sache return (temp); 40821308Sache} 40921308Sache 41021308Sache/* Get a history substitution string from STR starting at *IPTR 41121308Sache and return it. The length is returned in LENPTR. 41221308Sache 41321308Sache A backslash can quote the delimiter. If the string is the 41421308Sache empty string, the previous pattern is used. If there is 41521308Sache no previous pattern for the lhs, the last history search 41621308Sache string is used. 41721308Sache 41821308Sache If IS_RHS is 1, we ignore empty strings and set the pattern 41921308Sache to "" anyway. subst_lhs is not changed if the lhs is empty; 42021308Sache subst_rhs is allowed to be set to the empty string. */ 42121308Sache 42221308Sachestatic char * 42321308Sacheget_subst_pattern (str, iptr, delimiter, is_rhs, lenptr) 42421308Sache char *str; 42521308Sache int *iptr, delimiter, is_rhs, *lenptr; 42621308Sache{ 42721308Sache register int si, i, j, k; 428119610Sache char *s; 429119610Sache#if defined (HANDLE_MULTIBYTE) 430119610Sache mbstate_t ps; 431119610Sache#endif 43221308Sache 433119610Sache s = (char *)NULL; 43421308Sache i = *iptr; 43521308Sache 436119610Sache#if defined (HANDLE_MULTIBYTE) 437119610Sache memset (&ps, 0, sizeof (mbstate_t)); 438119610Sache _rl_adjust_point (str, i, &ps); 439119610Sache#endif 440119610Sache 44121308Sache for (si = i; str[si] && str[si] != delimiter; si++) 442119610Sache#if defined (HANDLE_MULTIBYTE) 443119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 444119610Sache { 445119610Sache int v; 446119610Sache if ((v = _rl_get_char_len (str + si, &ps)) > 1) 447119610Sache si += v - 1; 448119610Sache else if (str[si] == '\\' && str[si + 1] == delimiter) 449119610Sache si++; 450119610Sache } 451119610Sache else 452119610Sache#endif /* HANDLE_MULTIBYTE */ 453119610Sache if (str[si] == '\\' && str[si + 1] == delimiter) 454119610Sache si++; 45521308Sache 45621308Sache if (si > i || is_rhs) 45721308Sache { 458119610Sache s = (char *)xmalloc (si - i + 1); 45921308Sache for (j = 0, k = i; k < si; j++, k++) 46021308Sache { 46121308Sache /* Remove a backslash quoting the search string delimiter. */ 46221308Sache if (str[k] == '\\' && str[k + 1] == delimiter) 46321308Sache k++; 46421308Sache s[j] = str[k]; 46521308Sache } 46621308Sache s[j] = '\0'; 46721308Sache if (lenptr) 46821308Sache *lenptr = j; 46921308Sache } 47021308Sache 47121308Sache i = si; 47221308Sache if (str[i]) 47321308Sache i++; 47421308Sache *iptr = i; 47521308Sache 47621308Sache return s; 47721308Sache} 47821308Sache 47921308Sachestatic void 48021308Sachepostproc_subst_rhs () 48121308Sache{ 48221308Sache char *new; 48321308Sache int i, j, new_size; 48421308Sache 485119610Sache new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len); 48621308Sache for (i = j = 0; i < subst_rhs_len; i++) 48721308Sache { 48821308Sache if (subst_rhs[i] == '&') 48921308Sache { 49021308Sache if (j + subst_lhs_len >= new_size) 491119610Sache new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len)); 49221308Sache strcpy (new + j, subst_lhs); 49321308Sache j += subst_lhs_len; 49421308Sache } 49521308Sache else 49621308Sache { 49721308Sache /* a single backslash protects the `&' from lhs interpolation */ 49821308Sache if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&') 49921308Sache i++; 50021308Sache if (j >= new_size) 501119610Sache new = (char *)xrealloc (new, new_size *= 2); 50221308Sache new[j++] = subst_rhs[i]; 50321308Sache } 50421308Sache } 50521308Sache new[j] = '\0'; 50621308Sache free (subst_rhs); 50721308Sache subst_rhs = new; 50821308Sache subst_rhs_len = j; 50921308Sache} 51021308Sache 51121308Sache/* Expand the bulk of a history specifier starting at STRING[START]. 51221308Sache Returns 0 if everything is OK, -1 if an error occurred, and 1 51321308Sache if the `p' modifier was supplied and the caller should just print 51421308Sache the returned string. Returns the new index into string in 51521308Sache *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */ 51621308Sachestatic int 51721308Sachehistory_expand_internal (string, start, end_index_ptr, ret_string, current_line) 51821308Sache char *string; 51921308Sache int start, *end_index_ptr; 52021308Sache char **ret_string; 52121308Sache char *current_line; /* for !# */ 52221308Sache{ 52321308Sache int i, n, starting_index; 524136644Sache int substitute_globally, subst_bywords, want_quotes, print_only; 52521308Sache char *event, *temp, *result, *tstr, *t, c, *word_spec; 52621308Sache int result_len; 527119610Sache#if defined (HANDLE_MULTIBYTE) 528119610Sache mbstate_t ps; 52921308Sache 530119610Sache memset (&ps, 0, sizeof (mbstate_t)); 531119610Sache#endif 53221308Sache 533119610Sache result = (char *)xmalloc (result_len = 128); 534119610Sache 53521308Sache i = start; 53621308Sache 53721308Sache /* If it is followed by something that starts a word specifier, 53821308Sache then !! is implied as the event specifier. */ 53921308Sache 54021308Sache if (member (string[i + 1], ":$*%^")) 54121308Sache { 54221308Sache char fake_s[3]; 54321308Sache int fake_i = 0; 54421308Sache i++; 54521308Sache fake_s[0] = fake_s[1] = history_expansion_char; 54621308Sache fake_s[2] = '\0'; 54721308Sache event = get_history_event (fake_s, &fake_i, 0); 54821308Sache } 54921308Sache else if (string[i + 1] == '#') 55021308Sache { 55121308Sache i += 2; 55221308Sache event = current_line; 55321308Sache } 55421308Sache else 55521308Sache { 55621308Sache int quoted_search_delimiter = 0; 55721308Sache 55821308Sache /* If the character before this `!' is a double or single 55921308Sache quote, then this expansion takes place inside of the 56021308Sache quoted string. If we have to search for some text ("!foo"), 56121308Sache allow the delimiter to end the search string. */ 562119610Sache#if defined (HANDLE_MULTIBYTE) 563119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 564119610Sache { 565165670Sache int ch, l; 566119610Sache l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY); 567165670Sache ch = string[l]; 568119610Sache /* XXX - original patch had i - 1 ??? If i == 0 it would fail. */ 569165670Sache if (i && (ch == '\'' || ch == '"')) 570165670Sache quoted_search_delimiter = ch; 571119610Sache } 572119610Sache else 573119610Sache#endif /* HANDLE_MULTIBYTE */ 574119610Sache if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) 575119610Sache quoted_search_delimiter = string[i - 1]; 576119610Sache 57721308Sache event = get_history_event (string, &i, quoted_search_delimiter); 57821308Sache } 57921308Sache 58021308Sache if (event == 0) 58121308Sache { 58221308Sache *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND); 58321308Sache free (result); 58421308Sache return (-1); 58521308Sache } 58621308Sache 58721308Sache /* If a word specifier is found, then do what that requires. */ 58821308Sache starting_index = i; 58921308Sache word_spec = get_history_word_specifier (string, event, &i); 59021308Sache 59121308Sache /* There is no such thing as a `malformed word specifier'. However, 59221308Sache it is possible for a specifier that has no match. In that case, 59321308Sache we complain. */ 59421308Sache if (word_spec == (char *)&error_pointer) 59521308Sache { 59621308Sache *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC); 59721308Sache free (result); 59821308Sache return (-1); 59921308Sache } 60021308Sache 60121308Sache /* If no word specifier, than the thing of interest was the event. */ 60221308Sache temp = word_spec ? savestring (word_spec) : savestring (event); 60321308Sache FREE (word_spec); 60421308Sache 60521308Sache /* Perhaps there are other modifiers involved. Do what they say. */ 606136644Sache want_quotes = substitute_globally = subst_bywords = print_only = 0; 60721308Sache starting_index = i; 60821308Sache 60921308Sache while (string[i] == ':') 61021308Sache { 61121308Sache c = string[i + 1]; 61221308Sache 613136644Sache if (c == 'g' || c == 'a') 61421308Sache { 61521308Sache substitute_globally = 1; 61621308Sache i++; 61721308Sache c = string[i + 1]; 61821308Sache } 619136644Sache else if (c == 'G') 620136644Sache { 621136644Sache subst_bywords = 1; 622136644Sache i++; 623136644Sache c = string[i + 1]; 624136644Sache } 62521308Sache 62621308Sache switch (c) 62721308Sache { 62821308Sache default: 62921308Sache *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER); 63021308Sache free (result); 63121308Sache free (temp); 63221308Sache return -1; 63321308Sache 63421308Sache case 'q': 63521308Sache want_quotes = 'q'; 63621308Sache break; 63721308Sache 63821308Sache case 'x': 63921308Sache want_quotes = 'x'; 64021308Sache break; 64121308Sache 64221308Sache /* :p means make this the last executed line. So we 64321308Sache return an error state after adding this line to the 64421308Sache history. */ 64521308Sache case 'p': 64621308Sache print_only++; 64721308Sache break; 64821308Sache 64921308Sache /* :t discards all but the last part of the pathname. */ 65021308Sache case 't': 65121308Sache tstr = strrchr (temp, '/'); 65221308Sache if (tstr) 65321308Sache { 65421308Sache tstr++; 65521308Sache t = savestring (tstr); 65621308Sache free (temp); 65721308Sache temp = t; 65821308Sache } 65921308Sache break; 66021308Sache 66121308Sache /* :h discards the last part of a pathname. */ 66221308Sache case 'h': 66321308Sache tstr = strrchr (temp, '/'); 66421308Sache if (tstr) 66521308Sache *tstr = '\0'; 66621308Sache break; 66721308Sache 66821308Sache /* :r discards the suffix. */ 66921308Sache case 'r': 67021308Sache tstr = strrchr (temp, '.'); 67121308Sache if (tstr) 67221308Sache *tstr = '\0'; 67321308Sache break; 67421308Sache 67521308Sache /* :e discards everything but the suffix. */ 67621308Sache case 'e': 67721308Sache tstr = strrchr (temp, '.'); 67821308Sache if (tstr) 67921308Sache { 68021308Sache t = savestring (tstr); 68121308Sache free (temp); 68221308Sache temp = t; 68321308Sache } 68421308Sache break; 68521308Sache 68621308Sache /* :s/this/that substitutes `that' for the first 68721308Sache occurrence of `this'. :gs/this/that substitutes `that' 68821308Sache for each occurrence of `this'. :& repeats the last 68921308Sache substitution. :g& repeats the last substitution 69021308Sache globally. */ 69121308Sache 69221308Sache case '&': 69321308Sache case 's': 69421308Sache { 69575406Sache char *new_event; 696136644Sache int delimiter, failed, si, l_temp, ws, we; 69721308Sache 69821308Sache if (c == 's') 69921308Sache { 70021308Sache if (i + 2 < (int)strlen (string)) 701119610Sache { 702119610Sache#if defined (HANDLE_MULTIBYTE) 703119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 704119610Sache { 705119610Sache _rl_adjust_point (string, i + 2, &ps); 706119610Sache if (_rl_get_char_len (string + i + 2, &ps) > 1) 707119610Sache delimiter = 0; 708119610Sache else 709119610Sache delimiter = string[i + 2]; 710119610Sache } 711119610Sache else 712119610Sache#endif /* HANDLE_MULTIBYTE */ 713119610Sache delimiter = string[i + 2]; 714119610Sache } 71521308Sache else 71621308Sache break; /* no search delimiter */ 71721308Sache 71821308Sache i += 3; 71921308Sache 72021308Sache t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len); 72121308Sache /* An empty substitution lhs with no previous substitution 72221308Sache uses the last search string as the lhs. */ 72321308Sache if (t) 72421308Sache { 72521308Sache FREE (subst_lhs); 72621308Sache subst_lhs = t; 72721308Sache } 72821308Sache else if (!subst_lhs) 72921308Sache { 73021308Sache if (search_string && *search_string) 73121308Sache { 73221308Sache subst_lhs = savestring (search_string); 73321308Sache subst_lhs_len = strlen (subst_lhs); 73421308Sache } 73521308Sache else 73621308Sache { 73721308Sache subst_lhs = (char *) NULL; 73821308Sache subst_lhs_len = 0; 73921308Sache } 74021308Sache } 74121308Sache 74221308Sache FREE (subst_rhs); 74321308Sache subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len); 74421308Sache 74521308Sache /* If `&' appears in the rhs, it's supposed to be replaced 74621308Sache with the lhs. */ 74721308Sache if (member ('&', subst_rhs)) 74821308Sache postproc_subst_rhs (); 74921308Sache } 75021308Sache else 75121308Sache i += 2; 75221308Sache 75347558Sache /* If there is no lhs, the substitution can't succeed. */ 75447558Sache if (subst_lhs_len == 0) 75547558Sache { 75647558Sache *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST); 75747558Sache free (result); 75847558Sache free (temp); 75947558Sache return -1; 76047558Sache } 76147558Sache 76221308Sache l_temp = strlen (temp); 76321308Sache /* Ignore impossible cases. */ 76421308Sache if (subst_lhs_len > l_temp) 76521308Sache { 76621308Sache *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); 76721308Sache free (result); 76821308Sache free (temp); 76921308Sache return (-1); 77021308Sache } 77121308Sache 77221308Sache /* Find the first occurrence of THIS in TEMP. */ 773136644Sache /* Substitute SUBST_RHS for SUBST_LHS in TEMP. There are three 774136644Sache cases to consider: 775136644Sache 776136644Sache 1. substitute_globally == subst_bywords == 0 777136644Sache 2. substitute_globally == 1 && subst_bywords == 0 778136644Sache 3. substitute_globally == 0 && subst_bywords == 1 779136644Sache 780136644Sache In the first case, we substitute for the first occurrence only. 781136644Sache In the second case, we substitute for every occurrence. 782136644Sache In the third case, we tokenize into words and substitute the 783136644Sache first occurrence of each word. */ 784136644Sache 785136644Sache si = we = 0; 78621308Sache for (failed = 1; (si + subst_lhs_len) <= l_temp; si++) 787136644Sache { 788136644Sache /* First skip whitespace and find word boundaries if 789136644Sache we're past the end of the word boundary we found 790136644Sache the last time. */ 791136644Sache if (subst_bywords && si > we) 792136644Sache { 793136644Sache for (; temp[si] && whitespace (temp[si]); si++) 794136644Sache ; 795136644Sache ws = si; 796136644Sache we = history_tokenize_word (temp, si); 797136644Sache } 79821308Sache 799136644Sache if (STREQN (temp+si, subst_lhs, subst_lhs_len)) 800136644Sache { 801136644Sache int len = subst_rhs_len - subst_lhs_len + l_temp; 802136644Sache new_event = (char *)xmalloc (1 + len); 803136644Sache strncpy (new_event, temp, si); 804136644Sache strncpy (new_event + si, subst_rhs, subst_rhs_len); 805136644Sache strncpy (new_event + si + subst_rhs_len, 806136644Sache temp + si + subst_lhs_len, 807136644Sache l_temp - (si + subst_lhs_len)); 808136644Sache new_event[len] = '\0'; 809136644Sache free (temp); 810136644Sache temp = new_event; 81121308Sache 812136644Sache failed = 0; 81321308Sache 814136644Sache if (substitute_globally) 815136644Sache { 816136644Sache /* Reported to fix a bug that causes it to skip every 817136644Sache other match when matching a single character. Was 818136644Sache si += subst_rhs_len previously. */ 819136644Sache si += subst_rhs_len - 1; 820136644Sache l_temp = strlen (temp); 821136644Sache substitute_globally++; 822136644Sache continue; 823136644Sache } 824136644Sache else if (subst_bywords) 825136644Sache { 826136644Sache si = we; 827136644Sache l_temp = strlen (temp); 828136644Sache continue; 829136644Sache } 830136644Sache else 831136644Sache break; 832136644Sache } 833136644Sache } 834136644Sache 83521308Sache if (substitute_globally > 1) 83621308Sache { 83721308Sache substitute_globally = 0; 83821308Sache continue; /* don't want to increment i */ 83921308Sache } 84021308Sache 84121308Sache if (failed == 0) 84221308Sache continue; /* don't want to increment i */ 84321308Sache 84421308Sache *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); 84521308Sache free (result); 84621308Sache free (temp); 84721308Sache return (-1); 84821308Sache } 84921308Sache } 85021308Sache i += 2; 85121308Sache } 85221308Sache /* Done with modfiers. */ 85321308Sache /* Believe it or not, we have to back the pointer up by one. */ 85421308Sache --i; 85521308Sache 85621308Sache if (want_quotes) 85721308Sache { 85821308Sache char *x; 85921308Sache 86021308Sache if (want_quotes == 'q') 86175406Sache x = sh_single_quote (temp); 86221308Sache else if (want_quotes == 'x') 86321308Sache x = quote_breaks (temp); 86421308Sache else 86521308Sache x = savestring (temp); 86621308Sache 86721308Sache free (temp); 86821308Sache temp = x; 86921308Sache } 87021308Sache 87121308Sache n = strlen (temp); 87221308Sache if (n >= result_len) 873119610Sache result = (char *)xrealloc (result, n + 2); 87421308Sache strcpy (result, temp); 87521308Sache free (temp); 87621308Sache 87721308Sache *end_index_ptr = i; 87821308Sache *ret_string = result; 87921308Sache return (print_only); 88021308Sache} 88121308Sache 88221308Sache/* Expand the string STRING, placing the result into OUTPUT, a pointer 88321308Sache to a string. Returns: 88421308Sache 88521308Sache -1) If there was an error in expansion. 88621308Sache 0) If no expansions took place (or, if the only change in 88721308Sache the text was the de-slashifying of the history expansion 88821308Sache character) 88921308Sache 1) If expansions did take place 89021308Sache 2) If the `p' modifier was given and the caller should print the result 89121308Sache 89221308Sache If an error ocurred in expansion, then OUTPUT contains a descriptive 89321308Sache error message. */ 89421308Sache 89521308Sache#define ADD_STRING(s) \ 89621308Sache do \ 89721308Sache { \ 89821308Sache int sl = strlen (s); \ 89921308Sache j += sl; \ 90021308Sache if (j >= result_len) \ 90121308Sache { \ 90221308Sache while (j >= result_len) \ 90321308Sache result_len += 128; \ 904119610Sache result = (char *)xrealloc (result, result_len); \ 90521308Sache } \ 90621308Sache strcpy (result + j - sl, s); \ 90721308Sache } \ 90821308Sache while (0) 90921308Sache 91021308Sache#define ADD_CHAR(c) \ 91121308Sache do \ 91221308Sache { \ 91321308Sache if (j >= result_len - 1) \ 914119610Sache result = (char *)xrealloc (result, result_len += 64); \ 91521308Sache result[j++] = c; \ 91621308Sache result[j] = '\0'; \ 91721308Sache } \ 91821308Sache while (0) 91921308Sache 92021308Sacheint 92121308Sachehistory_expand (hstring, output) 92221308Sache char *hstring; 92321308Sache char **output; 92421308Sache{ 92521308Sache register int j; 926136644Sache int i, r, l, passc, cc, modified, eindex, only_printing, dquote; 92721308Sache char *string; 92821308Sache 92921308Sache /* The output string, and its length. */ 93021308Sache int result_len; 93121308Sache char *result; 93221308Sache 933119610Sache#if defined (HANDLE_MULTIBYTE) 934119610Sache char mb[MB_LEN_MAX]; 935119610Sache mbstate_t ps; 936119610Sache#endif 937119610Sache 93821308Sache /* Used when adding the string. */ 93921308Sache char *temp; 94021308Sache 94175406Sache if (output == 0) 94275406Sache return 0; 94375406Sache 94421308Sache /* Setting the history expansion character to 0 inhibits all 94521308Sache history expansion. */ 94621308Sache if (history_expansion_char == 0) 94721308Sache { 94821308Sache *output = savestring (hstring); 94921308Sache return (0); 95021308Sache } 95121308Sache 95221308Sache /* Prepare the buffer for printing error messages. */ 953119610Sache result = (char *)xmalloc (result_len = 256); 95421308Sache result[0] = '\0'; 95521308Sache 95621308Sache only_printing = modified = 0; 95721308Sache l = strlen (hstring); 95821308Sache 95935486Sache /* Grovel the string. Only backslash and single quotes can quote the 96035486Sache history escape character. We also handle arg specifiers. */ 96121308Sache 96221308Sache /* Before we grovel forever, see if the history_expansion_char appears 96321308Sache anywhere within the text. */ 96421308Sache 96521308Sache /* The quick substitution character is a history expansion all right. That 96621308Sache is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact, 96721308Sache that is the substitution that we do. */ 96821308Sache if (hstring[0] == history_subst_char) 96921308Sache { 970119610Sache string = (char *)xmalloc (l + 5); 97121308Sache 97221308Sache string[0] = string[1] = history_expansion_char; 97321308Sache string[2] = ':'; 97421308Sache string[3] = 's'; 97521308Sache strcpy (string + 4, hstring); 97621308Sache l += 4; 97721308Sache } 97821308Sache else 97921308Sache { 980119610Sache#if defined (HANDLE_MULTIBYTE) 981119610Sache memset (&ps, 0, sizeof (mbstate_t)); 982119610Sache#endif 983119610Sache 98421308Sache string = hstring; 98521308Sache /* If not quick substitution, still maybe have to do expansion. */ 98621308Sache 98721308Sache /* `!' followed by one of the characters in history_no_expand_chars 98821308Sache is NOT an expansion. */ 989136644Sache for (i = dquote = 0; string[i]; i++) 99021308Sache { 991119610Sache#if defined (HANDLE_MULTIBYTE) 992119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 993119610Sache { 994119610Sache int v; 995119610Sache v = _rl_get_char_len (string + i, &ps); 996119610Sache if (v > 1) 997119610Sache { 998119610Sache i += v - 1; 999119610Sache continue; 1000119610Sache } 1001119610Sache } 1002119610Sache#endif /* HANDLE_MULTIBYTE */ 1003119610Sache 100421308Sache cc = string[i + 1]; 1005119610Sache /* The history_comment_char, if set, appearing at the beginning 100635486Sache of a word signifies that the rest of the line should not have 100735486Sache history expansion performed on it. 100835486Sache Skip the rest of the line and break out of the loop. */ 100935486Sache if (history_comment_char && string[i] == history_comment_char && 101075406Sache (i == 0 || member (string[i - 1], history_word_delimiters))) 101121308Sache { 101235486Sache while (string[i]) 101335486Sache i++; 101435486Sache break; 101535486Sache } 101635486Sache else if (string[i] == history_expansion_char) 101735486Sache { 101821308Sache if (!cc || member (cc, history_no_expand_chars)) 101921308Sache continue; 102026497Sache /* If the calling application has set 102126497Sache history_inhibit_expansion_function to a function that checks 102226497Sache for special cases that should not be history expanded, 102326497Sache call the function and skip the expansion if it returns a 102426497Sache non-zero value. */ 102526497Sache else if (history_inhibit_expansion_function && 102626497Sache (*history_inhibit_expansion_function) (string, i)) 102721308Sache continue; 102821308Sache else 102921308Sache break; 103021308Sache } 1031136644Sache /* Shell-like quoting: allow backslashes to quote double quotes 1032136644Sache inside a double-quoted string. */ 1033136644Sache else if (dquote && string[i] == '\\' && cc == '"') 1034136644Sache i++; 1035136644Sache /* More shell-like quoting: if we're paying attention to single 1036136644Sache quotes and letting them quote the history expansion character, 1037136644Sache then we need to pay attention to double quotes, because single 1038136644Sache quotes are not special inside double-quoted strings. */ 1039136644Sache else if (history_quotes_inhibit_expansion && string[i] == '"') 104021308Sache { 1041136644Sache dquote = 1 - dquote; 1042136644Sache } 1043136644Sache else if (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'') 1044136644Sache { 104521308Sache /* If this is bash, single quotes inhibit history expansion. */ 104621308Sache i++; 104721308Sache hist_string_extract_single_quoted (string, &i); 104821308Sache } 104921308Sache else if (history_quotes_inhibit_expansion && string[i] == '\\') 105021308Sache { 105121308Sache /* If this is bash, allow backslashes to quote single 105221308Sache quotes and the history expansion character. */ 105321308Sache if (cc == '\'' || cc == history_expansion_char) 105421308Sache i++; 105521308Sache } 1056136644Sache 105721308Sache } 105821308Sache 105921308Sache if (string[i] != history_expansion_char) 106021308Sache { 106121308Sache free (result); 106221308Sache *output = savestring (string); 106321308Sache return (0); 106421308Sache } 106521308Sache } 106621308Sache 106721308Sache /* Extract and perform the substitution. */ 1068136644Sache for (passc = dquote = i = j = 0; i < l; i++) 106921308Sache { 107021308Sache int tchar = string[i]; 107121308Sache 107221308Sache if (passc) 107321308Sache { 107421308Sache passc = 0; 107521308Sache ADD_CHAR (tchar); 107621308Sache continue; 107721308Sache } 107821308Sache 1079119610Sache#if defined (HANDLE_MULTIBYTE) 1080119610Sache if (MB_CUR_MAX > 1 && rl_byte_oriented == 0) 1081119610Sache { 1082119610Sache int k, c; 1083119610Sache 1084119610Sache c = tchar; 1085119610Sache memset (mb, 0, sizeof (mb)); 1086119610Sache for (k = 0; k < MB_LEN_MAX; k++) 1087119610Sache { 1088119610Sache mb[k] = (char)c; 1089119610Sache memset (&ps, 0, sizeof (mbstate_t)); 1090119610Sache if (_rl_get_char_len (mb, &ps) == -2) 1091119610Sache c = string[++i]; 1092119610Sache else 1093119610Sache break; 1094119610Sache } 1095119610Sache if (strlen (mb) > 1) 1096119610Sache { 1097119610Sache ADD_STRING (mb); 1098119610Sache break; 1099119610Sache } 1100119610Sache } 1101119610Sache#endif /* HANDLE_MULTIBYTE */ 1102119610Sache 110321308Sache if (tchar == history_expansion_char) 110421308Sache tchar = -3; 110535486Sache else if (tchar == history_comment_char) 110635486Sache tchar = -2; 110721308Sache 110821308Sache switch (tchar) 110921308Sache { 111021308Sache default: 111121308Sache ADD_CHAR (string[i]); 111221308Sache break; 111321308Sache 111421308Sache case '\\': 111521308Sache passc++; 111621308Sache ADD_CHAR (tchar); 111721308Sache break; 111821308Sache 1119136644Sache case '"': 1120136644Sache dquote = 1 - dquote; 1121136644Sache ADD_CHAR (tchar); 1122136644Sache break; 1123136644Sache 112421308Sache case '\'': 112521308Sache { 112621308Sache /* If history_quotes_inhibit_expansion is set, single quotes 112721308Sache inhibit history expansion. */ 1128136644Sache if (dquote == 0 && history_quotes_inhibit_expansion) 112921308Sache { 113021308Sache int quote, slen; 113121308Sache 113221308Sache quote = i++; 113321308Sache hist_string_extract_single_quoted (string, &i); 113421308Sache 113521308Sache slen = i - quote + 2; 1136119610Sache temp = (char *)xmalloc (slen); 113721308Sache strncpy (temp, string + quote, slen); 113821308Sache temp[slen - 1] = '\0'; 113921308Sache ADD_STRING (temp); 114021308Sache free (temp); 114121308Sache } 114221308Sache else 114321308Sache ADD_CHAR (string[i]); 114421308Sache break; 114521308Sache } 114621308Sache 114735486Sache case -2: /* history_comment_char */ 114875406Sache if (i == 0 || member (string[i - 1], history_word_delimiters)) 114935486Sache { 1150119610Sache temp = (char *)xmalloc (l - i + 1); 115135486Sache strcpy (temp, string + i); 115235486Sache ADD_STRING (temp); 115335486Sache free (temp); 115435486Sache i = l; 115535486Sache } 115635486Sache else 115735486Sache ADD_CHAR (string[i]); 115835486Sache break; 115935486Sache 116021308Sache case -3: /* history_expansion_char */ 116121308Sache cc = string[i + 1]; 116221308Sache 116321308Sache /* If the history_expansion_char is followed by one of the 116421308Sache characters in history_no_expand_chars, then it is not a 116521308Sache candidate for expansion of any kind. */ 116621308Sache if (member (cc, history_no_expand_chars)) 116721308Sache { 116821308Sache ADD_CHAR (string[i]); 116921308Sache break; 117021308Sache } 117121308Sache 117221308Sache#if defined (NO_BANG_HASH_MODIFIERS) 117321308Sache /* There is something that is listed as a `word specifier' in csh 117421308Sache documentation which means `the expanded text to this point'. 117521308Sache That is not a word specifier, it is an event specifier. If we 117621308Sache don't want to allow modifiers with `!#', just stick the current 117721308Sache output line in again. */ 117821308Sache if (cc == '#') 117921308Sache { 118021308Sache if (result) 118121308Sache { 1182119610Sache temp = (char *)xmalloc (1 + strlen (result)); 118321308Sache strcpy (temp, result); 118421308Sache ADD_STRING (temp); 118521308Sache free (temp); 118621308Sache } 118721308Sache i++; 118821308Sache break; 118921308Sache } 119021308Sache#endif 119121308Sache 119221308Sache r = history_expand_internal (string, i, &eindex, &temp, result); 119321308Sache if (r < 0) 119421308Sache { 119521308Sache *output = temp; 119621308Sache free (result); 119721308Sache if (string != hstring) 119821308Sache free (string); 119921308Sache return -1; 120021308Sache } 120121308Sache else 120221308Sache { 120321308Sache if (temp) 120421308Sache { 120521308Sache modified++; 120621308Sache if (*temp) 120721308Sache ADD_STRING (temp); 120821308Sache free (temp); 120921308Sache } 121021308Sache only_printing = r == 1; 121121308Sache i = eindex; 121221308Sache } 121321308Sache break; 121421308Sache } 121521308Sache } 121621308Sache 121721308Sache *output = result; 121821308Sache if (string != hstring) 121921308Sache free (string); 122021308Sache 122121308Sache if (only_printing) 122221308Sache { 1223136644Sache#if 0 122421308Sache add_history (result); 1225136644Sache#endif 122621308Sache return (2); 122721308Sache } 122821308Sache 122921308Sache return (modified != 0); 123021308Sache} 123121308Sache 123221308Sache/* Return a consed string which is the word specified in SPEC, and found 123321308Sache in FROM. NULL is returned if there is no spec. The address of 123421308Sache ERROR_POINTER is returned if the word specified cannot be found. 123521308Sache CALLER_INDEX is the offset in SPEC to start looking; it is updated 123621308Sache to point to just after the last character parsed. */ 123721308Sachestatic char * 123821308Sacheget_history_word_specifier (spec, from, caller_index) 123921308Sache char *spec, *from; 124021308Sache int *caller_index; 124121308Sache{ 124221308Sache register int i = *caller_index; 124321308Sache int first, last; 124421308Sache int expecting_word_spec = 0; 124521308Sache char *result; 124621308Sache 124721308Sache /* The range of words to return doesn't exist yet. */ 124821308Sache first = last = 0; 124921308Sache result = (char *)NULL; 125021308Sache 125121308Sache /* If we found a colon, then this *must* be a word specification. If 125221308Sache it isn't, then it is an error. */ 125321308Sache if (spec[i] == ':') 125421308Sache { 125521308Sache i++; 125621308Sache expecting_word_spec++; 125721308Sache } 125821308Sache 125921308Sache /* Handle special cases first. */ 126021308Sache 126121308Sache /* `%' is the word last searched for. */ 126221308Sache if (spec[i] == '%') 126321308Sache { 126421308Sache *caller_index = i + 1; 126521308Sache return (search_match ? savestring (search_match) : savestring ("")); 126621308Sache } 126721308Sache 126821308Sache /* `*' matches all of the arguments, but not the command. */ 126921308Sache if (spec[i] == '*') 127021308Sache { 127121308Sache *caller_index = i + 1; 127221308Sache result = history_arg_extract (1, '$', from); 127321308Sache return (result ? result : savestring ("")); 127421308Sache } 127521308Sache 127621308Sache /* `$' is last arg. */ 127721308Sache if (spec[i] == '$') 127821308Sache { 127921308Sache *caller_index = i + 1; 128021308Sache return (history_arg_extract ('$', '$', from)); 128121308Sache } 128221308Sache 128321308Sache /* Try to get FIRST and LAST figured out. */ 128421308Sache 128521308Sache if (spec[i] == '-') 128621308Sache first = 0; 128721308Sache else if (spec[i] == '^') 1288136644Sache { 1289136644Sache first = 1; 1290136644Sache i++; 1291136644Sache } 129221308Sache else if (_rl_digit_p (spec[i]) && expecting_word_spec) 129321308Sache { 129421308Sache for (first = 0; _rl_digit_p (spec[i]); i++) 129521308Sache first = (first * 10) + _rl_digit_value (spec[i]); 129621308Sache } 129721308Sache else 129821308Sache return ((char *)NULL); /* no valid `first' for word specifier */ 129921308Sache 130021308Sache if (spec[i] == '^' || spec[i] == '*') 130121308Sache { 130221308Sache last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */ 130321308Sache i++; 130421308Sache } 130521308Sache else if (spec[i] != '-') 130621308Sache last = first; 130721308Sache else 130821308Sache { 130921308Sache i++; 131021308Sache 131121308Sache if (_rl_digit_p (spec[i])) 131221308Sache { 131321308Sache for (last = 0; _rl_digit_p (spec[i]); i++) 131421308Sache last = (last * 10) + _rl_digit_value (spec[i]); 131521308Sache } 131621308Sache else if (spec[i] == '$') 131721308Sache { 131821308Sache i++; 131921308Sache last = '$'; 132021308Sache } 1321119610Sache#if 0 1322119610Sache else if (!spec[i] || spec[i] == ':') 1323119610Sache /* check against `:' because there could be a modifier separator */ 1324119610Sache#else 1325119610Sache else 1326119610Sache /* csh seems to allow anything to terminate the word spec here, 1327119610Sache leaving it as an abbreviation. */ 1328119610Sache#endif 132921308Sache last = -1; /* x- abbreviates x-$ omitting word `$' */ 133021308Sache } 133121308Sache 133221308Sache *caller_index = i; 133321308Sache 133421308Sache if (last >= first || last == '$' || last < 0) 133521308Sache result = history_arg_extract (first, last, from); 133621308Sache 133721308Sache return (result ? result : (char *)&error_pointer); 133821308Sache} 133921308Sache 134021308Sache/* Extract the args specified, starting at FIRST, and ending at LAST. 134121308Sache The args are taken from STRING. If either FIRST or LAST is < 0, 134221308Sache then make that arg count from the right (subtract from the number of 134321308Sache tokens, so that FIRST = -1 means the next to last token on the line). 134421308Sache If LAST is `$' the last arg from STRING is used. */ 134521308Sachechar * 134621308Sachehistory_arg_extract (first, last, string) 134721308Sache int first, last; 134875406Sache const char *string; 134921308Sache{ 135021308Sache register int i, len; 135121308Sache char *result; 135221308Sache int size, offset; 135321308Sache char **list; 135421308Sache 135521308Sache /* XXX - think about making history_tokenize return a struct array, 135621308Sache each struct in array being a string and a length to avoid the 135721308Sache calls to strlen below. */ 135821308Sache if ((list = history_tokenize (string)) == NULL) 135921308Sache return ((char *)NULL); 136021308Sache 136121308Sache for (len = 0; list[len]; len++) 136221308Sache ; 136321308Sache 136421308Sache if (last < 0) 136521308Sache last = len + last - 1; 136621308Sache 136721308Sache if (first < 0) 136821308Sache first = len + first - 1; 136921308Sache 137021308Sache if (last == '$') 137121308Sache last = len - 1; 137221308Sache 137321308Sache if (first == '$') 137421308Sache first = len - 1; 137521308Sache 137621308Sache last++; 137721308Sache 137821308Sache if (first >= len || last > len || first < 0 || last < 0 || first > last) 137921308Sache result = ((char *)NULL); 138021308Sache else 138121308Sache { 138221308Sache for (size = 0, i = first; i < last; i++) 138321308Sache size += strlen (list[i]) + 1; 1384119610Sache result = (char *)xmalloc (size + 1); 138521308Sache result[0] = '\0'; 138621308Sache 138721308Sache for (i = first, offset = 0; i < last; i++) 138821308Sache { 138921308Sache strcpy (result + offset, list[i]); 139021308Sache offset += strlen (list[i]); 139121308Sache if (i + 1 < last) 139221308Sache { 139321308Sache result[offset++] = ' '; 139421308Sache result[offset] = 0; 139521308Sache } 139621308Sache } 139721308Sache } 139821308Sache 139921308Sache for (i = 0; i < len; i++) 140021308Sache free (list[i]); 140121308Sache free (list); 140221308Sache 140321308Sache return (result); 140421308Sache} 140521308Sache 1406136644Sachestatic int 1407136644Sachehistory_tokenize_word (string, ind) 1408136644Sache const char *string; 1409136644Sache int ind; 1410136644Sache{ 1411136644Sache register int i; 1412136644Sache int delimiter; 141321308Sache 1414136644Sache i = ind; 1415136644Sache delimiter = 0; 1416136644Sache 1417136644Sache if (member (string[i], "()\n")) 1418136644Sache { 1419136644Sache i++; 1420136644Sache return i; 1421136644Sache } 1422136644Sache 1423136644Sache if (member (string[i], "<>;&|$")) 1424136644Sache { 1425136644Sache int peek = string[i + 1]; 1426136644Sache 1427136644Sache if (peek == string[i] && peek != '$') 1428136644Sache { 1429136644Sache if (peek == '<' && string[i + 2] == '-') 1430136644Sache i++; 1431165670Sache else if (peek == '<' && string[i + 2] == '<') 1432165670Sache i++; 1433136644Sache i += 2; 1434136644Sache return i; 1435136644Sache } 1436136644Sache else 1437136644Sache { 1438136644Sache if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || 1439136644Sache (peek == '>' && string[i] == '&') || 1440136644Sache (peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */ 1441136644Sache (peek == '(' && string[i] == '$')) /* ) */ 1442136644Sache { 1443136644Sache i += 2; 1444136644Sache return i; 1445136644Sache } 1446136644Sache } 1447136644Sache 1448136644Sache if (string[i] != '$') 1449136644Sache { 1450136644Sache i++; 1451136644Sache return i; 1452136644Sache } 1453136644Sache } 1454136644Sache 1455136644Sache /* Get word from string + i; */ 1456136644Sache 1457136644Sache if (member (string[i], HISTORY_QUOTE_CHARACTERS)) 1458136644Sache delimiter = string[i++]; 1459136644Sache 1460136644Sache for (; string[i]; i++) 1461136644Sache { 1462136644Sache if (string[i] == '\\' && string[i + 1] == '\n') 1463136644Sache { 1464136644Sache i++; 1465136644Sache continue; 1466136644Sache } 1467136644Sache 1468136644Sache if (string[i] == '\\' && delimiter != '\'' && 1469136644Sache (delimiter != '"' || member (string[i], slashify_in_quotes))) 1470136644Sache { 1471136644Sache i++; 1472136644Sache continue; 1473136644Sache } 1474136644Sache 1475136644Sache if (delimiter && string[i] == delimiter) 1476136644Sache { 1477136644Sache delimiter = 0; 1478136644Sache continue; 1479136644Sache } 1480136644Sache 1481136644Sache if (!delimiter && (member (string[i], history_word_delimiters))) 1482136644Sache break; 1483136644Sache 1484136644Sache if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)) 1485136644Sache delimiter = string[i]; 1486136644Sache } 1487136644Sache 1488136644Sache return i; 1489136644Sache} 1490136644Sache 1491136644Sachestatic char * 1492136644Sachehistory_substring (string, start, end) 1493136644Sache const char *string; 1494136644Sache int start, end; 1495136644Sache{ 1496136644Sache register int len; 1497136644Sache register char *result; 1498136644Sache 1499136644Sache len = end - start; 1500136644Sache result = (char *)xmalloc (len + 1); 1501136644Sache strncpy (result, string + start, len); 1502136644Sache result[len] = '\0'; 1503136644Sache return result; 1504136644Sache} 1505136644Sache 150621308Sache/* Parse STRING into tokens and return an array of strings. If WIND is 150721308Sache not -1 and INDP is not null, we also want the word surrounding index 150821308Sache WIND. The position in the returned array of strings is returned in 150921308Sache *INDP. */ 151021308Sachestatic char ** 151121308Sachehistory_tokenize_internal (string, wind, indp) 151275406Sache const char *string; 151321308Sache int wind, *indp; 151421308Sache{ 151521308Sache char **result; 151621308Sache register int i, start, result_index, size; 151721308Sache 151875406Sache /* If we're searching for a string that's not part of a word (e.g., " "), 151975406Sache make sure we set *INDP to a reasonable value. */ 152075406Sache if (indp && wind != -1) 152175406Sache *indp = -1; 152275406Sache 152321308Sache /* Get a token, and stuff it into RESULT. The tokens are split 152421308Sache exactly where the shell would split them. */ 152521308Sache for (i = result_index = size = 0, result = (char **)NULL; string[i]; ) 152621308Sache { 152721308Sache /* Skip leading whitespace. */ 152821308Sache for (; string[i] && whitespace (string[i]); i++) 152921308Sache ; 153021308Sache if (string[i] == 0 || string[i] == history_comment_char) 153121308Sache return (result); 153221308Sache 153321308Sache start = i; 153421308Sache 1535136644Sache i = history_tokenize_word (string, start); 153621308Sache 1537136644Sache /* If we have a non-whitespace delimiter character (which would not be 1538136644Sache skipped by the loop above), use it and any adjacent delimiters to 1539136644Sache make a separate field. Any adjacent white space will be skipped the 1540136644Sache next time through the loop. */ 1541136644Sache if (i == start && history_word_delimiters) 154221308Sache { 1543136644Sache i++; 1544136644Sache while (string[i] && member (string[i], history_word_delimiters)) 1545136644Sache i++; 154621308Sache } 154721308Sache 154821308Sache /* If we are looking for the word in which the character at a 154921308Sache particular index falls, remember it. */ 155021308Sache if (indp && wind != -1 && wind >= start && wind < i) 155121308Sache *indp = result_index; 155221308Sache 155321308Sache if (result_index + 2 >= size) 155421308Sache result = (char **)xrealloc (result, ((size += 10) * sizeof (char *))); 1555136644Sache 1556136644Sache result[result_index++] = history_substring (string, start, i); 1557136644Sache result[result_index] = (char *)NULL; 155821308Sache } 155921308Sache 156021308Sache return (result); 156121308Sache} 156221308Sache 156321308Sache/* Return an array of tokens, much as the shell might. The tokens are 156421308Sache parsed out of STRING. */ 156521308Sachechar ** 156621308Sachehistory_tokenize (string) 156775406Sache const char *string; 156821308Sache{ 156921308Sache return (history_tokenize_internal (string, -1, (int *)NULL)); 157021308Sache} 157121308Sache 157221308Sache/* Find and return the word which contains the character at index IND 157321308Sache in the history line LINE. Used to save the word matched by the 157421308Sache last history !?string? search. */ 157521308Sachestatic char * 157621308Sachehistory_find_word (line, ind) 157721308Sache char *line; 157821308Sache int ind; 157921308Sache{ 158021308Sache char **words, *s; 158121308Sache int i, wind; 158221308Sache 158321308Sache words = history_tokenize_internal (line, ind, &wind); 158475406Sache if (wind == -1 || words == 0) 158521308Sache return ((char *)NULL); 158621308Sache s = words[wind]; 158721308Sache for (i = 0; i < wind; i++) 158821308Sache free (words[i]); 158921308Sache for (i = wind + 1; words[i]; i++) 159021308Sache free (words[i]); 159121308Sache free (words); 159221308Sache return s; 159321308Sache} 1594