1
/* histexpand.c -- history expansion. */
3
/* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
5
This file contains the GNU History Library (the Library), a set of
6
routines for managing the text of previously typed lines.
8
The Library is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 2, or (at your option)
13
The Library is distributed in the hope that it will be useful, but
14
WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
General Public License for more details.
18
The GNU General Public License is often shipped with GNU software, and
19
is generally kept in a file called COPYING or LICENSE. If you do not
20
have a copy of the license, write to the Free Software Foundation,
21
59 Temple Place, Suite 330, Boston, MA 02111 USA. */
23
#define READLINE_LIBRARY
25
#if defined (HAVE_CONFIG_H)
31
#if defined (HAVE_STDLIB_H)
34
# include "ansi_stdlib.h"
35
#endif /* HAVE_STDLIB_H */
37
#if defined (HAVE_UNISTD_H)
39
# include <sys/types.h>
52
#define HISTORY_WORD_DELIMITERS " \t\n;&()|<>"
53
#define HISTORY_QUOTE_CHARACTERS "\"'`"
55
typedef int _hist_search_func_t PARAMS((const char *, int));
57
extern int rl_byte_oriented; /* declared in mbutil.c */
59
static char error_pointer;
61
static char *subst_lhs;
62
static char *subst_rhs;
63
static int subst_lhs_len;
64
static int subst_rhs_len;
66
static char *get_history_word_specifier PARAMS((char *, char *, int *));
67
static char *history_find_word PARAMS((char *, int));
69
static char *quote_breaks PARAMS((char *));
71
/* Variables exported by this file. */
72
/* The character that represents the start of a history expansion
73
request. This is usually `!'. */
74
char history_expansion_char = '!';
76
/* The character that invokes word substitution if found at the start of
77
a line. This is usually `^'. */
78
char history_subst_char = '^';
80
/* During tokenization, if this character is seen as the first character
81
of a word, then it, and all subsequent characters upto a newline are
82
ignored. For a Bourne shell, this should be '#'. Bash special cases
83
the interactive comment character to not be a comment delimiter. */
84
char history_comment_char = '\0';
86
/* The list of characters which inhibit the expansion of text if found
87
immediately following history_expansion_char. */
88
char *history_no_expand_chars = " \t\n\r=";
90
/* If set to a non-zero value, single quotes inhibit history expansion.
92
int history_quotes_inhibit_expansion = 0;
94
/* Used to split words by history_tokenize_internal. */
95
char *history_word_delimiters = HISTORY_WORD_DELIMITERS;
97
/* If set, this points to a function that is called to verify that a
98
particular history expansion should be performed. */
99
rl_linebuf_func_t *history_inhibit_expansion_function;
101
/* **************************************************************** */
103
/* History Expansion */
105
/* **************************************************************** */
107
/* Hairy history expansion on text, not tokens. This is of general
108
use, and thus belongs in this library. */
110
/* The last string searched for by a !?string? search. */
111
static char *search_string;
113
/* The last string matched by a !?string? search. */
114
static char *search_match;
116
/* Return the event specified at TEXT + OFFSET modifying OFFSET to
117
point to after the event specifier. Just a pointer to the history
118
line is returned; NULL is returned in the event of a bad specifier.
119
You pass STRING with *INDEX equal to the history_expansion_char that
120
begins this specification.
121
DELIMITING_QUOTE is a character that is allowed to end the string
122
specification for what to search for in addition to the normal
123
characters `:', ` ', `\t', `\n', and sometimes `?'.
124
So you might call this function like:
125
line = get_history_event ("!echo:p", &index, 0); */
127
get_history_event (string, caller_index, delimiting_quote)
130
int delimiting_quote;
135
int which, sign, local_index, substring_okay;
136
_hist_search_func_t *search_func;
139
/* The event can be specified in a number of ways.
141
!! the previous command
143
!-n current command-line minus N
144
!str the most recent command starting with STR
146
the most recent command containing STR
148
All values N are determined via HISTORY_BASE. */
152
if (string[i] != history_expansion_char)
153
return ((char *)NULL);
155
/* Move on to the specification. */
161
#define RETURN_ENTRY(e, w) \
162
return ((e = history_get (w)) ? e->line : (char *)NULL)
164
/* Handle !! case. */
165
if (string[i] == history_expansion_char)
168
which = history_base + (history_length - 1);
170
RETURN_ENTRY (entry, which);
173
/* Hack case of numeric line specification. */
174
if (string[i] == '-')
180
if (_rl_digit_p (string[i]))
182
/* Get the extent of the digits and compute the value. */
183
for (which = 0; _rl_digit_p (string[i]); i++)
184
which = (which * 10) + _rl_digit_value (string[i]);
189
which = (history_length + history_base) - which;
191
RETURN_ENTRY (entry, which);
194
/* This must be something to search for. If the spec begins with
195
a '?', then the string may be anywhere on the line. Otherwise,
196
the string must be found at the start of a line. */
197
if (string[i] == '?')
203
/* Only a closing `?' or a newline delimit a substring search string. */
204
for (local_index = i; c = string[i]; i++)
205
#if defined (HANDLE_MULTIBYTE)
206
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
211
memset (&ps, 0, sizeof (mbstate_t));
212
/* These produce warnings because we're passing a const string to a
213
function that takes a non-const string. */
214
_rl_adjust_point (string, i, &ps);
215
if ((v = _rl_get_char_len (string + i, &ps)) > 1)
222
#endif /* HANDLE_MULTIBYTE */
223
if ((!substring_okay && (whitespace (c) || c == ':' ||
224
(history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
225
string[i] == delimiting_quote)) ||
227
(substring_okay && string[i] == '?'))
230
which = i - local_index;
231
temp = (char *)xmalloc (1 + which);
233
strncpy (temp, string + local_index, which);
236
if (substring_okay && string[i] == '?')
241
#define FAIL_SEARCH() \
243
history_offset = history_length; free (temp) ; return (char *)NULL; \
246
/* If there is no search string, try to use the previous search string,
247
if one exists. If not, fail immediately. */
248
if (*temp == '\0' && substring_okay)
253
temp = savestring (search_string);
259
search_func = substring_okay ? history_search : history_search_prefix;
262
local_index = (*search_func) (temp, -1);
267
if (local_index == 0 || substring_okay)
269
entry = current_history ();
270
history_offset = history_length;
272
/* If this was a substring search, then remember the
273
string that we matched for word substitution. */
276
FREE (search_string);
277
search_string = temp;
280
search_match = history_find_word (entry->line, local_index);
285
return (entry->line);
297
/* Function for extracting single-quoted strings. Used for inhibiting
298
history expansion within single quotes. */
300
/* Extract the contents of STRING as if it is enclosed in single quotes.
301
SINDEX, when passed in, is the offset of the character immediately
302
following the opening single quote; on exit, SINDEX is left pointing
303
to the closing single quote. */
305
hist_string_extract_single_quoted (string, sindex)
311
for (i = *sindex; string[i] && string[i] != '\''; i++)
321
register char *p, *r;
325
for (p = s; p && *p; p++, len++)
329
else if (whitespace (*p) || *p == '\n')
333
r = ret = (char *)xmalloc (len);
335
for (p = s; p && *p; )
345
else if (whitespace (*p) || *p == '\n')
360
hist_error(s, start, current, errtype)
362
int start, current, errtype;
368
ll = current - start;
372
case EVENT_NOT_FOUND:
373
emsg = "event not found";
377
emsg = "bad word specifier";
381
emsg = "substitution failed";
385
emsg = "unrecognized history modifier";
389
emsg = "no previous substitution";
393
emsg = "unknown expansion error";
398
temp = (char *)xmalloc (ll + elen + 3);
399
strncpy (temp, s + start, ll);
402
strcpy (temp + ll + 2, emsg);
406
/* Get a history substitution string from STR starting at *IPTR
407
and return it. The length is returned in LENPTR.
409
A backslash can quote the delimiter. If the string is the
410
empty string, the previous pattern is used. If there is
411
no previous pattern for the lhs, the last history search
414
If IS_RHS is 1, we ignore empty strings and set the pattern
415
to "" anyway. subst_lhs is not changed if the lhs is empty;
416
subst_rhs is allowed to be set to the empty string. */
419
get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
421
int *iptr, delimiter, is_rhs, *lenptr;
423
register int si, i, j, k;
425
#if defined (HANDLE_MULTIBYTE)
432
#if defined (HANDLE_MULTIBYTE)
433
memset (&ps, 0, sizeof (mbstate_t));
434
_rl_adjust_point (str, i, &ps);
437
for (si = i; str[si] && str[si] != delimiter; si++)
438
#if defined (HANDLE_MULTIBYTE)
439
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
442
if ((v = _rl_get_char_len (str + si, &ps)) > 1)
444
else if (str[si] == '\\' && str[si + 1] == delimiter)
448
#endif /* HANDLE_MULTIBYTE */
449
if (str[si] == '\\' && str[si + 1] == delimiter)
452
if (si > i || is_rhs)
454
s = (char *)xmalloc (si - i + 1);
455
for (j = 0, k = i; k < si; j++, k++)
457
/* Remove a backslash quoting the search string delimiter. */
458
if (str[k] == '\\' && str[k + 1] == delimiter)
476
postproc_subst_rhs ()
481
new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len);
482
for (i = j = 0; i < subst_rhs_len; i++)
484
if (subst_rhs[i] == '&')
486
if (j + subst_lhs_len >= new_size)
487
new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
488
strcpy (new + j, subst_lhs);
493
/* a single backslash protects the `&' from lhs interpolation */
494
if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
497
new = (char *)xrealloc (new, new_size *= 2);
498
new[j++] = subst_rhs[i];
507
/* Expand the bulk of a history specifier starting at STRING[START].
508
Returns 0 if everything is OK, -1 if an error occurred, and 1
509
if the `p' modifier was supplied and the caller should just print
510
the returned string. Returns the new index into string in
511
*END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
513
history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
515
int start, *end_index_ptr;
517
char *current_line; /* for !# */
519
int i, n, starting_index;
520
int substitute_globally, want_quotes, print_only;
521
char *event, *temp, *result, *tstr, *t, c, *word_spec;
523
#if defined (HANDLE_MULTIBYTE)
526
memset (&ps, 0, sizeof (mbstate_t));
529
result = (char *)xmalloc (result_len = 128);
533
/* If it is followed by something that starts a word specifier,
534
then !! is implied as the event specifier. */
536
if (member (string[i + 1], ":$*%^"))
541
fake_s[0] = fake_s[1] = history_expansion_char;
543
event = get_history_event (fake_s, &fake_i, 0);
545
else if (string[i + 1] == '#')
548
event = current_line;
552
int quoted_search_delimiter = 0;
554
/* If the character before this `!' is a double or single
555
quote, then this expansion takes place inside of the
556
quoted string. If we have to search for some text ("!foo"),
557
allow the delimiter to end the search string. */
558
#if defined (HANDLE_MULTIBYTE)
559
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
562
l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY);
564
/* XXX - original patch had i - 1 ??? If i == 0 it would fail. */
565
if (i && (c == '\'' || c == '"'))
566
quoted_search_delimiter = c;
569
#endif /* HANDLE_MULTIBYTE */
570
if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
571
quoted_search_delimiter = string[i - 1];
573
event = get_history_event (string, &i, quoted_search_delimiter);
578
*ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
583
/* If a word specifier is found, then do what that requires. */
585
word_spec = get_history_word_specifier (string, event, &i);
587
/* There is no such thing as a `malformed word specifier'. However,
588
it is possible for a specifier that has no match. In that case,
590
if (word_spec == (char *)&error_pointer)
592
*ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
597
/* If no word specifier, than the thing of interest was the event. */
598
temp = word_spec ? savestring (word_spec) : savestring (event);
601
/* Perhaps there are other modifiers involved. Do what they say. */
602
want_quotes = substitute_globally = print_only = 0;
605
while (string[i] == ':')
611
substitute_globally = 1;
619
*ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
632
/* :p means make this the last executed line. So we
633
return an error state after adding this line to the
639
/* :t discards all but the last part of the pathname. */
641
tstr = strrchr (temp, '/');
645
t = savestring (tstr);
651
/* :h discards the last part of a pathname. */
653
tstr = strrchr (temp, '/');
658
/* :r discards the suffix. */
660
tstr = strrchr (temp, '.');
665
/* :e discards everything but the suffix. */
667
tstr = strrchr (temp, '.');
670
t = savestring (tstr);
676
/* :s/this/that substitutes `that' for the first
677
occurrence of `this'. :gs/this/that substitutes `that'
678
for each occurrence of `this'. :& repeats the last
679
substitution. :g& repeats the last substitution
686
int delimiter, failed, si, l_temp;
690
if (i + 2 < (int)strlen (string))
692
#if defined (HANDLE_MULTIBYTE)
693
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
695
_rl_adjust_point (string, i + 2, &ps);
696
if (_rl_get_char_len (string + i + 2, &ps) > 1)
699
delimiter = string[i + 2];
702
#endif /* HANDLE_MULTIBYTE */
703
delimiter = string[i + 2];
706
break; /* no search delimiter */
710
t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
711
/* An empty substitution lhs with no previous substitution
712
uses the last search string as the lhs. */
720
if (search_string && *search_string)
722
subst_lhs = savestring (search_string);
723
subst_lhs_len = strlen (subst_lhs);
727
subst_lhs = (char *) NULL;
733
subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
735
/* If `&' appears in the rhs, it's supposed to be replaced
737
if (member ('&', subst_rhs))
738
postproc_subst_rhs ();
743
/* If there is no lhs, the substitution can't succeed. */
744
if (subst_lhs_len == 0)
746
*ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
752
l_temp = strlen (temp);
753
/* Ignore impossible cases. */
754
if (subst_lhs_len > l_temp)
756
*ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
762
/* Find the first occurrence of THIS in TEMP. */
764
for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
765
if (STREQN (temp+si, subst_lhs, subst_lhs_len))
767
int len = subst_rhs_len - subst_lhs_len + l_temp;
768
new_event = (char *)xmalloc (1 + len);
769
strncpy (new_event, temp, si);
770
strncpy (new_event + si, subst_rhs, subst_rhs_len);
771
strncpy (new_event + si + subst_rhs_len,
772
temp + si + subst_lhs_len,
773
l_temp - (si + subst_lhs_len));
774
new_event[len] = '\0';
780
if (substitute_globally)
783
l_temp = strlen (temp);
784
substitute_globally++;
791
if (substitute_globally > 1)
793
substitute_globally = 0;
794
continue; /* don't want to increment i */
798
continue; /* don't want to increment i */
800
*ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
808
/* Done with modfiers. */
809
/* Believe it or not, we have to back the pointer up by one. */
816
if (want_quotes == 'q')
817
x = sh_single_quote (temp);
818
else if (want_quotes == 'x')
819
x = quote_breaks (temp);
821
x = savestring (temp);
829
result = (char *)xrealloc (result, n + 2);
830
strcpy (result, temp);
834
*ret_string = result;
838
/* Expand the string STRING, placing the result into OUTPUT, a pointer
839
to a string. Returns:
841
-1) If there was an error in expansion.
842
0) If no expansions took place (or, if the only change in
843
the text was the de-slashifying of the history expansion
845
1) If expansions did take place
846
2) If the `p' modifier was given and the caller should print the result
848
If an error ocurred in expansion, then OUTPUT contains a descriptive
851
#define ADD_STRING(s) \
854
int sl = strlen (s); \
856
if (j >= result_len) \
858
while (j >= result_len) \
860
result = (char *)xrealloc (result, result_len); \
862
strcpy (result + j - sl, s); \
866
#define ADD_CHAR(c) \
869
if (j >= result_len - 1) \
870
result = (char *)xrealloc (result, result_len += 64); \
877
history_expand (hstring, output)
882
int i, r, l, passc, cc, modified, eindex, only_printing;
885
/* The output string, and its length. */
889
#if defined (HANDLE_MULTIBYTE)
894
/* Used when adding the string. */
900
/* Setting the history expansion character to 0 inhibits all
901
history expansion. */
902
if (history_expansion_char == 0)
904
*output = savestring (hstring);
908
/* Prepare the buffer for printing error messages. */
909
result = (char *)xmalloc (result_len = 256);
912
only_printing = modified = 0;
913
l = strlen (hstring);
915
/* Grovel the string. Only backslash and single quotes can quote the
916
history escape character. We also handle arg specifiers. */
918
/* Before we grovel forever, see if the history_expansion_char appears
919
anywhere within the text. */
921
/* The quick substitution character is a history expansion all right. That
922
is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
923
that is the substitution that we do. */
924
if (hstring[0] == history_subst_char)
926
string = (char *)xmalloc (l + 5);
928
string[0] = string[1] = history_expansion_char;
931
strcpy (string + 4, hstring);
936
#if defined (HANDLE_MULTIBYTE)
937
memset (&ps, 0, sizeof (mbstate_t));
941
/* If not quick substitution, still maybe have to do expansion. */
943
/* `!' followed by one of the characters in history_no_expand_chars
944
is NOT an expansion. */
945
for (i = 0; string[i]; i++)
947
#if defined (HANDLE_MULTIBYTE)
948
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
951
v = _rl_get_char_len (string + i, &ps);
958
#endif /* HANDLE_MULTIBYTE */
961
/* The history_comment_char, if set, appearing at the beginning
962
of a word signifies that the rest of the line should not have
963
history expansion performed on it.
964
Skip the rest of the line and break out of the loop. */
965
if (history_comment_char && string[i] == history_comment_char &&
966
(i == 0 || member (string[i - 1], history_word_delimiters)))
972
else if (string[i] == history_expansion_char)
974
if (!cc || member (cc, history_no_expand_chars))
976
/* If the calling application has set
977
history_inhibit_expansion_function to a function that checks
978
for special cases that should not be history expanded,
979
call the function and skip the expansion if it returns a
981
else if (history_inhibit_expansion_function &&
982
(*history_inhibit_expansion_function) (string, i))
987
/* XXX - at some point, might want to extend this to handle
988
double quotes as well. */
989
else if (history_quotes_inhibit_expansion && string[i] == '\'')
991
/* If this is bash, single quotes inhibit history expansion. */
993
hist_string_extract_single_quoted (string, &i);
995
else if (history_quotes_inhibit_expansion && string[i] == '\\')
997
/* If this is bash, allow backslashes to quote single
998
quotes and the history expansion character. */
999
if (cc == '\'' || cc == history_expansion_char)
1004
if (string[i] != history_expansion_char)
1007
*output = savestring (string);
1012
/* Extract and perform the substitution. */
1013
for (passc = i = j = 0; i < l; i++)
1015
int tchar = string[i];
1024
#if defined (HANDLE_MULTIBYTE)
1025
if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
1030
memset (mb, 0, sizeof (mb));
1031
for (k = 0; k < MB_LEN_MAX; k++)
1034
memset (&ps, 0, sizeof (mbstate_t));
1035
if (_rl_get_char_len (mb, &ps) == -2)
1040
if (strlen (mb) > 1)
1046
#endif /* HANDLE_MULTIBYTE */
1048
if (tchar == history_expansion_char)
1050
else if (tchar == history_comment_char)
1056
ADD_CHAR (string[i]);
1066
/* If history_quotes_inhibit_expansion is set, single quotes
1067
inhibit history expansion. */
1068
if (history_quotes_inhibit_expansion)
1073
hist_string_extract_single_quoted (string, &i);
1075
slen = i - quote + 2;
1076
temp = (char *)xmalloc (slen);
1077
strncpy (temp, string + quote, slen);
1078
temp[slen - 1] = '\0';
1083
ADD_CHAR (string[i]);
1087
case -2: /* history_comment_char */
1088
if (i == 0 || member (string[i - 1], history_word_delimiters))
1090
temp = (char *)xmalloc (l - i + 1);
1091
strcpy (temp, string + i);
1097
ADD_CHAR (string[i]);
1100
case -3: /* history_expansion_char */
1103
/* If the history_expansion_char is followed by one of the
1104
characters in history_no_expand_chars, then it is not a
1105
candidate for expansion of any kind. */
1106
if (member (cc, history_no_expand_chars))
1108
ADD_CHAR (string[i]);
1112
#if defined (NO_BANG_HASH_MODIFIERS)
1113
/* There is something that is listed as a `word specifier' in csh
1114
documentation which means `the expanded text to this point'.
1115
That is not a word specifier, it is an event specifier. If we
1116
don't want to allow modifiers with `!#', just stick the current
1117
output line in again. */
1122
temp = (char *)xmalloc (1 + strlen (result));
1123
strcpy (temp, result);
1132
r = history_expand_internal (string, i, &eindex, &temp, result);
1137
if (string != hstring)
1150
only_printing = r == 1;
1158
if (string != hstring)
1163
add_history (result);
1167
return (modified != 0);
1170
/* Return a consed string which is the word specified in SPEC, and found
1171
in FROM. NULL is returned if there is no spec. The address of
1172
ERROR_POINTER is returned if the word specified cannot be found.
1173
CALLER_INDEX is the offset in SPEC to start looking; it is updated
1174
to point to just after the last character parsed. */
1176
get_history_word_specifier (spec, from, caller_index)
1180
register int i = *caller_index;
1182
int expecting_word_spec = 0;
1185
/* The range of words to return doesn't exist yet. */
1187
result = (char *)NULL;
1189
/* If we found a colon, then this *must* be a word specification. If
1190
it isn't, then it is an error. */
1194
expecting_word_spec++;
1197
/* Handle special cases first. */
1199
/* `%' is the word last searched for. */
1202
*caller_index = i + 1;
1203
return (search_match ? savestring (search_match) : savestring (""));
1206
/* `*' matches all of the arguments, but not the command. */
1209
*caller_index = i + 1;
1210
result = history_arg_extract (1, '$', from);
1211
return (result ? result : savestring (""));
1214
/* `$' is last arg. */
1217
*caller_index = i + 1;
1218
return (history_arg_extract ('$', '$', from));
1221
/* Try to get FIRST and LAST figured out. */
1225
else if (spec[i] == '^')
1227
else if (_rl_digit_p (spec[i]) && expecting_word_spec)
1229
for (first = 0; _rl_digit_p (spec[i]); i++)
1230
first = (first * 10) + _rl_digit_value (spec[i]);
1233
return ((char *)NULL); /* no valid `first' for word specifier */
1235
if (spec[i] == '^' || spec[i] == '*')
1237
last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */
1240
else if (spec[i] != '-')
1246
if (_rl_digit_p (spec[i]))
1248
for (last = 0; _rl_digit_p (spec[i]); i++)
1249
last = (last * 10) + _rl_digit_value (spec[i]);
1251
else if (spec[i] == '$')
1257
else if (!spec[i] || spec[i] == ':')
1258
/* check against `:' because there could be a modifier separator */
1261
/* csh seems to allow anything to terminate the word spec here,
1262
leaving it as an abbreviation. */
1264
last = -1; /* x- abbreviates x-$ omitting word `$' */
1269
if (last >= first || last == '$' || last < 0)
1270
result = history_arg_extract (first, last, from);
1272
return (result ? result : (char *)&error_pointer);
1275
/* Extract the args specified, starting at FIRST, and ending at LAST.
1276
The args are taken from STRING. If either FIRST or LAST is < 0,
1277
then make that arg count from the right (subtract from the number of
1278
tokens, so that FIRST = -1 means the next to last token on the line).
1279
If LAST is `$' the last arg from STRING is used. */
1281
history_arg_extract (first, last, string)
1285
register int i, len;
1290
/* XXX - think about making history_tokenize return a struct array,
1291
each struct in array being a string and a length to avoid the
1292
calls to strlen below. */
1293
if ((list = history_tokenize (string)) == NULL)
1294
return ((char *)NULL);
1296
for (len = 0; list[len]; len++)
1300
last = len + last - 1;
1303
first = len + first - 1;
1313
if (first >= len || last > len || first < 0 || last < 0 || first > last)
1314
result = ((char *)NULL);
1317
for (size = 0, i = first; i < last; i++)
1318
size += strlen (list[i]) + 1;
1319
result = (char *)xmalloc (size + 1);
1322
for (i = first, offset = 0; i < last; i++)
1324
strcpy (result + offset, list[i]);
1325
offset += strlen (list[i]);
1328
result[offset++] = ' ';
1334
for (i = 0; i < len; i++)
1341
#define slashify_in_quotes "\\`\"$"
1343
/* Parse STRING into tokens and return an array of strings. If WIND is
1344
not -1 and INDP is not null, we also want the word surrounding index
1345
WIND. The position in the returned array of strings is returned in
1348
history_tokenize_internal (string, wind, indp)
1353
register int i, start, result_index, size;
1356
/* If we're searching for a string that's not part of a word (e.g., " "),
1357
make sure we set *INDP to a reasonable value. */
1358
if (indp && wind != -1)
1361
/* Get a token, and stuff it into RESULT. The tokens are split
1362
exactly where the shell would split them. */
1363
for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
1367
/* Skip leading whitespace. */
1368
for (; string[i] && whitespace (string[i]); i++)
1370
if (string[i] == 0 || string[i] == history_comment_char)
1375
if (member (string[i], "()\n"))
1381
if (member (string[i], "<>;&|$"))
1383
int peek = string[i + 1];
1385
if (peek == string[i] && peek != '$')
1387
if (peek == '<' && string[i + 2] == '-')
1394
if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
1395
((peek == '>') && (string[i] == '&')) ||
1396
((peek == '(') && (string[i] == '$')))
1402
if (string[i] != '$')
1409
/* Get word from string + i; */
1411
if (member (string[i], HISTORY_QUOTE_CHARACTERS))
1412
delimiter = string[i++];
1414
for (; string[i]; i++)
1416
if (string[i] == '\\' && string[i + 1] == '\n')
1422
if (string[i] == '\\' && delimiter != '\'' &&
1423
(delimiter != '"' || member (string[i], slashify_in_quotes)))
1429
if (delimiter && string[i] == delimiter)
1435
if (!delimiter && (member (string[i], history_word_delimiters)))
1438
if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
1439
delimiter = string[i];
1444
/* If we are looking for the word in which the character at a
1445
particular index falls, remember it. */
1446
if (indp && wind != -1 && wind >= start && wind < i)
1447
*indp = result_index;
1450
if (result_index + 2 >= size)
1451
result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
1452
result[result_index] = (char *)xmalloc (1 + len);
1453
strncpy (result[result_index], string + start, len);
1454
result[result_index][len] = '\0';
1455
result[++result_index] = (char *)NULL;
1461
/* Return an array of tokens, much as the shell might. The tokens are
1462
parsed out of STRING. */
1464
history_tokenize (string)
1467
return (history_tokenize_internal (string, -1, (int *)NULL));
1470
/* Find and return the word which contains the character at index IND
1471
in the history line LINE. Used to save the word matched by the
1472
last history !?string? search. */
1474
history_find_word (line, ind)
1481
words = history_tokenize_internal (line, ind, &wind);
1482
if (wind == -1 || words == 0)
1483
return ((char *)NULL);
1485
for (i = 0; i < wind; i++)
1487
for (i = wind + 1; words[i]; i++)