File: | src/gnu/lib/libreadline/histexpand.c |
Warning: | line 1186, column 3 Value stored to 'first' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* histexpand.c -- history expansion. */ |
2 | |
3 | /* Copyright (C) 1989, 1992 Free Software Foundation, Inc. |
4 | |
5 | This file contains the GNU History Library (the Library), a set of |
6 | routines for managing the text of previously typed lines. |
7 | |
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) |
11 | any later version. |
12 | |
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. |
17 | |
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. */ |
22 | |
23 | #define READLINE_LIBRARY |
24 | |
25 | #if defined (HAVE_CONFIG_H1) |
26 | # include <config.h> |
27 | #endif |
28 | |
29 | #include <stdio.h> |
30 | |
31 | #if defined (HAVE_STDLIB_H1) |
32 | # include <stdlib.h> |
33 | #else |
34 | # include "ansi_stdlib.h" |
35 | #endif /* HAVE_STDLIB_H */ |
36 | |
37 | #if defined (HAVE_UNISTD_H1) |
38 | # ifndef _MINIX |
39 | # include <sys/types.h> |
40 | # endif |
41 | # include <unistd.h> |
42 | #endif |
43 | |
44 | #include "rlmbutil.h" |
45 | |
46 | #include "history.h" |
47 | #include "histlib.h" |
48 | |
49 | #include "rlshell.h" |
50 | #include "xmalloc.h" |
51 | |
52 | #define HISTORY_WORD_DELIMITERS" \t\n;&()|<>" " \t\n;&()|<>" |
53 | #define HISTORY_QUOTE_CHARACTERS"\"'`" "\"'`" |
54 | |
55 | typedef int _hist_search_func_t PARAMS((const char *, int))(const char *, int); |
56 | |
57 | extern int rl_byte_oriented; /* declared in mbutil.c */ |
58 | |
59 | static char error_pointer; |
60 | |
61 | static char *subst_lhs; |
62 | static char *subst_rhs; |
63 | static int subst_lhs_len; |
64 | static int subst_rhs_len; |
65 | |
66 | static char *get_history_word_specifier PARAMS((char *, char *, int *))(char *, char *, int *); |
67 | static char *history_find_word PARAMS((char *, int))(char *, int); |
68 | |
69 | static char *quote_breaks PARAMS((char *))(char *); |
70 | |
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 = '!'; |
75 | |
76 | /* The character that invokes word substitution if found at the start of |
77 | a line. This is usually `^'. */ |
78 | char history_subst_char = '^'; |
79 | |
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'; |
85 | |
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="; |
89 | |
90 | /* If set to a non-zero value, single quotes inhibit history expansion. |
91 | The default is 0. */ |
92 | int history_quotes_inhibit_expansion = 0; |
93 | |
94 | /* Used to split words by history_tokenize_internal. */ |
95 | char *history_word_delimiters = HISTORY_WORD_DELIMITERS" \t\n;&()|<>"; |
96 | |
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; |
100 | |
101 | /* **************************************************************** */ |
102 | /* */ |
103 | /* History Expansion */ |
104 | /* */ |
105 | /* **************************************************************** */ |
106 | |
107 | /* Hairy history expansion on text, not tokens. This is of general |
108 | use, and thus belongs in this library. */ |
109 | |
110 | /* The last string searched for by a !?string? search. */ |
111 | static char *search_string; |
112 | |
113 | /* The last string matched by a !?string? search. */ |
114 | static char *search_match; |
115 | |
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); */ |
126 | char * |
127 | get_history_event (string, caller_index, delimiting_quote) |
128 | const char *string; |
129 | int *caller_index; |
130 | int delimiting_quote; |
131 | { |
132 | register int i; |
133 | register char c; |
134 | HIST_ENTRY *entry; |
135 | int which, sign, local_index, substring_okay; |
136 | _hist_search_func_t *search_func; |
137 | char *temp; |
138 | |
139 | /* The event can be specified in a number of ways. |
140 | |
141 | !! the previous command |
142 | !n command line N |
143 | !-n current command-line minus N |
144 | !str the most recent command starting with STR |
145 | !?str[?] |
146 | the most recent command containing STR |
147 | |
148 | All values N are determined via HISTORY_BASE. */ |
149 | |
150 | i = *caller_index; |
151 | |
152 | if (string[i] != history_expansion_char) |
153 | return ((char *)NULL((void *)0)); |
154 | |
155 | /* Move on to the specification. */ |
156 | i++; |
157 | |
158 | sign = 1; |
159 | substring_okay = 0; |
160 | |
161 | #define RETURN_ENTRY(e, w) \ |
162 | return ((e = history_get (w)) ? e->line : (char *)NULL((void *)0)) |
163 | |
164 | /* Handle !! case. */ |
165 | if (string[i] == history_expansion_char) |
166 | { |
167 | i++; |
168 | which = history_base + (history_length - 1); |
169 | *caller_index = i; |
170 | RETURN_ENTRY (entry, which); |
171 | } |
172 | |
173 | /* Hack case of numeric line specification. */ |
174 | if (string[i] == '-') |
175 | { |
176 | sign = -1; |
177 | i++; |
178 | } |
179 | |
180 | if (_rl_digit_p (string[i])((string[i]) >= '0' && (string[i]) <= '9')) |
181 | { |
182 | /* Get the extent of the digits and compute the value. */ |
183 | for (which = 0; _rl_digit_p (string[i])((string[i]) >= '0' && (string[i]) <= '9'); i++) |
184 | which = (which * 10) + _rl_digit_value (string[i])((string[i]) - '0'); |
185 | |
186 | *caller_index = i; |
187 | |
188 | if (sign < 0) |
189 | which = (history_length + history_base) - which; |
190 | |
191 | RETURN_ENTRY (entry, which); |
192 | } |
193 | |
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] == '?') |
198 | { |
199 | substring_okay++; |
200 | i++; |
201 | } |
202 | |
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_MAX1 > 1 && rl_byte_oriented == 0) |
207 | { |
208 | int v; |
209 | mbstate_t ps; |
210 | |
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) |
216 | { |
217 | i += v - 1; |
218 | continue; |
219 | } |
220 | } |
221 | else |
222 | #endif /* HANDLE_MULTIBYTE */ |
223 | if ((!substring_okay && (whitespace (c)(((c) == ' ') || ((c) == '\t')) || c == ':' || |
224 | (history_search_delimiter_chars && member (c, history_search_delimiter_chars)((c) ? ((char *)strchr ((history_search_delimiter_chars), (c) ) != (char *)((void *)0)) : 0)) || |
225 | string[i] == delimiting_quote)) || |
226 | string[i] == '\n' || |
227 | (substring_okay && string[i] == '?')) |
228 | break; |
229 | |
230 | which = i - local_index; |
231 | temp = (char *)xmalloc (1 + which); |
232 | if (which) |
233 | strncpy (temp, string + local_index, which); |
234 | temp[which] = '\0'; |
235 | |
236 | if (substring_okay && string[i] == '?') |
237 | i++; |
238 | |
239 | *caller_index = i; |
240 | |
241 | #define FAIL_SEARCH() \ |
242 | do { \ |
243 | history_offset = history_length; free (temp) ; return (char *)NULL((void *)0); \ |
244 | } while (0) |
245 | |
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) |
249 | { |
250 | if (search_string) |
251 | { |
252 | free (temp); |
253 | temp = savestring (search_string)xstrdup(search_string); |
254 | } |
255 | else |
256 | FAIL_SEARCH (); |
257 | } |
258 | |
259 | search_func = substring_okay ? history_search : history_search_prefix; |
260 | while (1) |
261 | { |
262 | local_index = (*search_func) (temp, -1); |
263 | |
264 | if (local_index < 0) |
265 | FAIL_SEARCH (); |
266 | |
267 | if (local_index == 0 || substring_okay) |
268 | { |
269 | entry = current_history (); |
270 | history_offset = history_length; |
271 | |
272 | /* If this was a substring search, then remember the |
273 | string that we matched for word substitution. */ |
274 | if (substring_okay) |
275 | { |
276 | FREE (search_string)if (search_string) free (search_string); |
277 | search_string = temp; |
278 | |
279 | FREE (search_match)if (search_match) free (search_match); |
280 | search_match = history_find_word (entry->line, local_index); |
281 | } |
282 | else |
283 | free (temp); |
284 | |
285 | return (entry->line); |
286 | } |
287 | |
288 | if (history_offset) |
289 | history_offset--; |
290 | else |
291 | FAIL_SEARCH (); |
292 | } |
293 | #undef FAIL_SEARCH |
294 | #undef RETURN_ENTRY |
295 | } |
296 | |
297 | /* Function for extracting single-quoted strings. Used for inhibiting |
298 | history expansion within single quotes. */ |
299 | |
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. */ |
304 | static void |
305 | hist_string_extract_single_quoted (string, sindex) |
306 | char *string; |
307 | int *sindex; |
308 | { |
309 | register int i; |
310 | |
311 | for (i = *sindex; string[i] && string[i] != '\''; i++) |
312 | ; |
313 | |
314 | *sindex = i; |
315 | } |
316 | |
317 | static char * |
318 | quote_breaks (s) |
319 | char *s; |
320 | { |
321 | register char *p, *r; |
322 | char *ret; |
323 | int len = 3; |
324 | |
325 | for (p = s; p && *p; p++, len++) |
326 | { |
327 | if (*p == '\'') |
328 | len += 3; |
329 | else if (whitespace (*p)(((*p) == ' ') || ((*p) == '\t')) || *p == '\n') |
330 | len += 2; |
331 | } |
332 | |
333 | r = ret = (char *)xmalloc (len); |
334 | *r++ = '\''; |
335 | for (p = s; p && *p; ) |
336 | { |
337 | if (*p == '\'') |
338 | { |
339 | *r++ = '\''; |
340 | *r++ = '\\'; |
341 | *r++ = '\''; |
342 | *r++ = '\''; |
343 | p++; |
344 | } |
345 | else if (whitespace (*p)(((*p) == ' ') || ((*p) == '\t')) || *p == '\n') |
346 | { |
347 | *r++ = '\''; |
348 | *r++ = *p++; |
349 | *r++ = '\''; |
350 | } |
351 | else |
352 | *r++ = *p++; |
353 | } |
354 | *r++ = '\''; |
355 | *r = '\0'; |
356 | return ret; |
357 | } |
358 | |
359 | static char * |
360 | hist_error(s, start, current, errtype) |
361 | char *s; |
362 | int start, current, errtype; |
363 | { |
364 | char *temp; |
365 | const char *emsg; |
366 | int ll, elen, len; |
367 | |
368 | ll = current - start; |
369 | |
370 | switch (errtype) |
371 | { |
372 | case EVENT_NOT_FOUND0: |
373 | emsg = "event not found"; |
374 | elen = 15; |
375 | break; |
376 | case BAD_WORD_SPEC1: |
377 | emsg = "bad word specifier"; |
378 | elen = 18; |
379 | break; |
380 | case SUBST_FAILED2: |
381 | emsg = "substitution failed"; |
382 | elen = 19; |
383 | break; |
384 | case BAD_MODIFIER3: |
385 | emsg = "unrecognized history modifier"; |
386 | elen = 29; |
387 | break; |
388 | case NO_PREV_SUBST4: |
389 | emsg = "no previous substitution"; |
390 | elen = 24; |
391 | break; |
392 | default: |
393 | emsg = "unknown expansion error"; |
394 | elen = 23; |
395 | break; |
396 | } |
397 | |
398 | len = ll + elen + 3; |
399 | temp = (char *)xmalloc (len); |
400 | strncpy (temp, s + start, ll); |
401 | strlcat (temp, ": ", len); |
402 | strlcat (temp, emsg, len); |
403 | return (temp); |
404 | } |
405 | |
406 | /* Get a history substitution string from STR starting at *IPTR |
407 | and return it. The length is returned in LENPTR. |
408 | |
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 |
412 | string is used. |
413 | |
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. */ |
417 | |
418 | static char * |
419 | get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr) |
420 | char *str; |
421 | int *iptr, delimiter, is_rhs, *lenptr; |
422 | { |
423 | register int si, i, j, k; |
424 | char *s; |
425 | #if defined (HANDLE_MULTIBYTE) |
426 | mbstate_t ps; |
427 | #endif |
428 | |
429 | s = (char *)NULL((void *)0); |
430 | i = *iptr; |
431 | |
432 | #if defined (HANDLE_MULTIBYTE) |
433 | memset (&ps, 0, sizeof (mbstate_t)); |
434 | _rl_adjust_point (str, i, &ps); |
435 | #endif |
436 | |
437 | for (si = i; str[si] && str[si] != delimiter; si++) |
438 | #if defined (HANDLE_MULTIBYTE) |
439 | if (MB_CUR_MAX1 > 1 && rl_byte_oriented == 0) |
440 | { |
441 | int v; |
442 | if ((v = _rl_get_char_len (str + si, &ps)) > 1) |
443 | si += v - 1; |
444 | else if (str[si] == '\\' && str[si + 1] == delimiter) |
445 | si++; |
446 | } |
447 | else |
448 | #endif /* HANDLE_MULTIBYTE */ |
449 | if (str[si] == '\\' && str[si + 1] == delimiter) |
450 | si++; |
451 | |
452 | if (si > i || is_rhs) |
453 | { |
454 | s = (char *)xmalloc (si - i + 1); |
455 | for (j = 0, k = i; k < si; j++, k++) |
456 | { |
457 | /* Remove a backslash quoting the search string delimiter. */ |
458 | if (str[k] == '\\' && str[k + 1] == delimiter) |
459 | k++; |
460 | s[j] = str[k]; |
461 | } |
462 | s[j] = '\0'; |
463 | if (lenptr) |
464 | *lenptr = j; |
465 | } |
466 | |
467 | i = si; |
468 | if (str[i]) |
469 | i++; |
470 | *iptr = i; |
471 | |
472 | return s; |
473 | } |
474 | |
475 | static void |
476 | postproc_subst_rhs () |
477 | { |
478 | char *new; |
479 | int i, j, new_size; |
480 | |
481 | new = (char *)xmalloc (new_size = subst_rhs_len + subst_lhs_len); |
482 | for (i = j = 0; i < subst_rhs_len; i++) |
483 | { |
484 | if (subst_rhs[i] == '&') |
485 | { |
486 | if (j + subst_lhs_len >= new_size) |
487 | new = (char *)xrealloc (new, (new_size = new_size * 2 + subst_lhs_len)); |
488 | strlcpy (new + j, subst_lhs, new_size - j); |
489 | j += subst_lhs_len; |
490 | } |
491 | else |
492 | { |
493 | /* a single backslash protects the `&' from lhs interpolation */ |
494 | if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&') |
495 | i++; |
496 | if (j >= new_size) |
497 | new = (char *)xrealloc (new, new_size *= 2); |
498 | new[j++] = subst_rhs[i]; |
499 | } |
500 | } |
501 | new[j] = '\0'; |
502 | free (subst_rhs); |
503 | subst_rhs = new; |
504 | subst_rhs_len = j; |
505 | } |
506 | |
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. */ |
512 | static int |
513 | history_expand_internal (string, start, end_index_ptr, ret_string, current_line) |
514 | char *string; |
515 | int start, *end_index_ptr; |
516 | char **ret_string; |
517 | char *current_line; /* for !# */ |
518 | { |
519 | int i, n, starting_index; |
520 | int substitute_globally, want_quotes, print_only; |
521 | char *event, *temp, *result, *tstr, *t, c, *word_spec; |
522 | int result_len; |
523 | #if defined (HANDLE_MULTIBYTE) |
524 | mbstate_t ps; |
525 | |
526 | memset (&ps, 0, sizeof (mbstate_t)); |
527 | #endif |
528 | |
529 | result = (char *)xmalloc (result_len = 128); |
530 | |
531 | i = start; |
532 | |
533 | /* If it is followed by something that starts a word specifier, |
534 | then !! is implied as the event specifier. */ |
535 | |
536 | if (member (string[i + 1], ":$*%^")((string[i + 1]) ? ((char *)strchr ((":$*%^"), (string[i + 1] )) != (char *)((void *)0)) : 0)) |
537 | { |
538 | char fake_s[3]; |
539 | int fake_i = 0; |
540 | i++; |
541 | fake_s[0] = fake_s[1] = history_expansion_char; |
542 | fake_s[2] = '\0'; |
543 | event = get_history_event (fake_s, &fake_i, 0); |
544 | } |
545 | else if (string[i + 1] == '#') |
546 | { |
547 | i += 2; |
548 | event = current_line; |
549 | } |
550 | else |
551 | { |
552 | int quoted_search_delimiter = 0; |
553 | |
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_MAX1 > 1 && rl_byte_oriented == 0) |
560 | { |
561 | int c, l; |
562 | l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY)(((i) == 0) ? (i) : ((i) - 1)); |
563 | c = string[l]; |
564 | /* XXX - original patch had i - 1 ??? If i == 0 it would fail. */ |
565 | if (i && (c == '\'' || c == '"')) |
566 | quoted_search_delimiter = c; |
567 | } |
568 | else |
569 | #endif /* HANDLE_MULTIBYTE */ |
570 | if (i && (string[i - 1] == '\'' || string[i - 1] == '"')) |
571 | quoted_search_delimiter = string[i - 1]; |
572 | |
573 | event = get_history_event (string, &i, quoted_search_delimiter); |
574 | } |
575 | |
576 | if (event == 0) |
577 | { |
578 | *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND0); |
579 | free (result); |
580 | return (-1); |
581 | } |
582 | |
583 | /* If a word specifier is found, then do what that requires. */ |
584 | starting_index = i; |
585 | word_spec = get_history_word_specifier (string, event, &i); |
586 | |
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, |
589 | we complain. */ |
590 | if (word_spec == (char *)&error_pointer) |
591 | { |
592 | *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC1); |
593 | free (result); |
594 | return (-1); |
595 | } |
596 | |
597 | /* If no word specifier, than the thing of interest was the event. */ |
598 | temp = word_spec ? savestring (word_spec)xstrdup(word_spec) : savestring (event)xstrdup(event); |
599 | FREE (word_spec)if (word_spec) free (word_spec); |
600 | |
601 | /* Perhaps there are other modifiers involved. Do what they say. */ |
602 | want_quotes = substitute_globally = print_only = 0; |
603 | starting_index = i; |
604 | |
605 | while (string[i] == ':') |
606 | { |
607 | c = string[i + 1]; |
608 | |
609 | if (c == 'g') |
610 | { |
611 | substitute_globally = 1; |
612 | i++; |
613 | c = string[i + 1]; |
614 | } |
615 | |
616 | switch (c) |
617 | { |
618 | default: |
619 | *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER3); |
620 | free (result); |
621 | free (temp); |
622 | return -1; |
623 | |
624 | case 'q': |
625 | want_quotes = 'q'; |
626 | break; |
627 | |
628 | case 'x': |
629 | want_quotes = 'x'; |
630 | break; |
631 | |
632 | /* :p means make this the last executed line. So we |
633 | return an error state after adding this line to the |
634 | history. */ |
635 | case 'p': |
636 | print_only++; |
637 | break; |
638 | |
639 | /* :t discards all but the last part of the pathname. */ |
640 | case 't': |
641 | tstr = strrchr (temp, '/'); |
642 | if (tstr) |
643 | { |
644 | tstr++; |
645 | t = savestring (tstr)xstrdup(tstr); |
646 | free (temp); |
647 | temp = t; |
648 | } |
649 | break; |
650 | |
651 | /* :h discards the last part of a pathname. */ |
652 | case 'h': |
653 | tstr = strrchr (temp, '/'); |
654 | if (tstr) |
655 | *tstr = '\0'; |
656 | break; |
657 | |
658 | /* :r discards the suffix. */ |
659 | case 'r': |
660 | tstr = strrchr (temp, '.'); |
661 | if (tstr) |
662 | *tstr = '\0'; |
663 | break; |
664 | |
665 | /* :e discards everything but the suffix. */ |
666 | case 'e': |
667 | tstr = strrchr (temp, '.'); |
668 | if (tstr) |
669 | { |
670 | t = savestring (tstr)xstrdup(tstr); |
671 | free (temp); |
672 | temp = t; |
673 | } |
674 | break; |
675 | |
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 |
680 | globally. */ |
681 | |
682 | case '&': |
683 | case 's': |
684 | { |
685 | char *new_event; |
686 | int delimiter, failed, si, l_temp; |
687 | |
688 | if (c == 's') |
689 | { |
690 | if (i + 2 < (int)strlen (string)) |
691 | { |
692 | #if defined (HANDLE_MULTIBYTE) |
693 | if (MB_CUR_MAX1 > 1 && rl_byte_oriented == 0) |
694 | { |
695 | _rl_adjust_point (string, i + 2, &ps); |
696 | if (_rl_get_char_len (string + i + 2, &ps) > 1) |
697 | delimiter = 0; |
698 | else |
699 | delimiter = string[i + 2]; |
700 | } |
701 | else |
702 | #endif /* HANDLE_MULTIBYTE */ |
703 | delimiter = string[i + 2]; |
704 | } |
705 | else |
706 | break; /* no search delimiter */ |
707 | |
708 | i += 3; |
709 | |
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. */ |
713 | if (t) |
714 | { |
715 | FREE (subst_lhs)if (subst_lhs) free (subst_lhs); |
716 | subst_lhs = t; |
717 | } |
718 | else if (!subst_lhs) |
719 | { |
720 | if (search_string && *search_string) |
721 | { |
722 | subst_lhs = savestring (search_string)xstrdup(search_string); |
723 | subst_lhs_len = strlen (subst_lhs); |
724 | } |
725 | else |
726 | { |
727 | subst_lhs = (char *) NULL((void *)0); |
728 | subst_lhs_len = 0; |
729 | } |
730 | } |
731 | |
732 | FREE (subst_rhs)if (subst_rhs) free (subst_rhs); |
733 | subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len); |
734 | |
735 | /* If `&' appears in the rhs, it's supposed to be replaced |
736 | with the lhs. */ |
737 | if (member ('&', subst_rhs)(('&') ? ((char *)strchr ((subst_rhs), ('&')) != (char *)((void *)0)) : 0)) |
738 | postproc_subst_rhs (); |
739 | } |
740 | else |
741 | i += 2; |
742 | |
743 | /* If there is no lhs, the substitution can't succeed. */ |
744 | if (subst_lhs_len == 0) |
745 | { |
746 | *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST4); |
747 | free (result); |
748 | free (temp); |
749 | return -1; |
750 | } |
751 | |
752 | l_temp = strlen (temp); |
753 | /* Ignore impossible cases. */ |
754 | if (subst_lhs_len > l_temp) |
755 | { |
756 | *ret_string = hist_error (string, starting_index, i, SUBST_FAILED2); |
757 | free (result); |
758 | free (temp); |
759 | return (-1); |
760 | } |
761 | |
762 | /* Find the first occurrence of THIS in TEMP. */ |
763 | si = 0; |
764 | for (failed = 1; (si + subst_lhs_len) <= l_temp; si++) |
765 | if (STREQN (temp+si, subst_lhs, subst_lhs_len)(((subst_lhs_len) == 0) ? (1) : ((temp+si)[0] == (subst_lhs)[ 0]) && (strncmp ((temp+si), (subst_lhs), (subst_lhs_len )) == 0))) |
766 | { |
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'; |
775 | free (temp); |
776 | temp = new_event; |
777 | |
778 | failed = 0; |
779 | |
780 | if (substitute_globally) |
781 | { |
782 | si += subst_rhs_len; |
783 | l_temp = strlen (temp); |
784 | substitute_globally++; |
785 | continue; |
786 | } |
787 | else |
788 | break; |
789 | } |
790 | |
791 | if (substitute_globally > 1) |
792 | { |
793 | substitute_globally = 0; |
794 | continue; /* don't want to increment i */ |
795 | } |
796 | |
797 | if (failed == 0) |
798 | continue; /* don't want to increment i */ |
799 | |
800 | *ret_string = hist_error (string, starting_index, i, SUBST_FAILED2); |
801 | free (result); |
802 | free (temp); |
803 | return (-1); |
804 | } |
805 | } |
806 | i += 2; |
807 | } |
808 | /* Done with modfiers. */ |
809 | /* Believe it or not, we have to back the pointer up by one. */ |
810 | --i; |
811 | |
812 | if (want_quotes) |
813 | { |
814 | char *x; |
815 | |
816 | if (want_quotes == 'q') |
817 | x = sh_single_quote (temp); |
818 | else if (want_quotes == 'x') |
819 | x = quote_breaks (temp); |
820 | else |
821 | x = savestring (temp)xstrdup(temp); |
822 | |
823 | free (temp); |
824 | temp = x; |
825 | } |
826 | |
827 | n = strlen (temp); |
828 | if (n >= result_len) |
829 | result = (char *)xrealloc (result, n + 2); |
830 | strlcpy (result, temp, n + 2); |
831 | free (temp); |
832 | |
833 | *end_index_ptr = i; |
834 | *ret_string = result; |
835 | return (print_only); |
836 | } |
837 | |
838 | /* Expand the string STRING, placing the result into OUTPUT, a pointer |
839 | to a string. Returns: |
840 | |
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 |
844 | character) |
845 | 1) If expansions did take place |
846 | 2) If the `p' modifier was given and the caller should print the result |
847 | |
848 | If an error ocurred in expansion, then OUTPUT contains a descriptive |
849 | error message. */ |
850 | |
851 | #define ADD_STRING(s)do { int sl = strlen (s); j += sl; if (j >= result_len) { while (j >= result_len) result_len += 128; result = (char *)xrealloc (result, result_len); } strlcpy (result + j - sl, s, result_len - j + sl); } while (0) \ |
852 | do \ |
853 | { \ |
854 | int sl = strlen (s); \ |
855 | j += sl; \ |
856 | if (j >= result_len) \ |
857 | { \ |
858 | while (j >= result_len) \ |
859 | result_len += 128; \ |
860 | result = (char *)xrealloc (result, result_len); \ |
861 | } \ |
862 | strlcpy (result + j - sl, s, result_len - j + sl); \ |
863 | } \ |
864 | while (0) |
865 | |
866 | #define ADD_CHAR(c)do { if (j >= result_len - 1) result = (char *)xrealloc (result , result_len += 64); result[j++] = c; result[j] = '\0'; } while (0) \ |
867 | do \ |
868 | { \ |
869 | if (j >= result_len - 1) \ |
870 | result = (char *)xrealloc (result, result_len += 64); \ |
871 | result[j++] = c; \ |
872 | result[j] = '\0'; \ |
873 | } \ |
874 | while (0) |
875 | |
876 | int |
877 | history_expand (hstring, output) |
878 | char *hstring; |
879 | char **output; |
880 | { |
881 | register int j; |
882 | int i, r, l, passc, cc, modified, eindex, only_printing; |
883 | char *string; |
884 | |
885 | /* The output string, and its length. */ |
886 | int result_len; |
887 | char *result; |
888 | |
889 | #if defined (HANDLE_MULTIBYTE) |
890 | char mb[MB_LEN_MAX1]; |
891 | mbstate_t ps; |
892 | #endif |
893 | |
894 | /* Used when adding the string. */ |
895 | char *temp; |
896 | |
897 | if (output == 0) |
898 | return 0; |
899 | |
900 | /* Setting the history expansion character to 0 inhibits all |
901 | history expansion. */ |
902 | if (history_expansion_char == 0) |
903 | { |
904 | *output = savestring (hstring)xstrdup(hstring); |
905 | return (0); |
906 | } |
907 | |
908 | /* Prepare the buffer for printing error messages. */ |
909 | result = (char *)xmalloc (result_len = 256); |
910 | result[0] = '\0'; |
911 | |
912 | only_printing = modified = 0; |
913 | l = strlen (hstring); |
914 | |
915 | /* Grovel the string. Only backslash and single quotes can quote the |
916 | history escape character. We also handle arg specifiers. */ |
917 | |
918 | /* Before we grovel forever, see if the history_expansion_char appears |
919 | anywhere within the text. */ |
920 | |
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) |
925 | { |
926 | string = (char *)xmalloc (l + 5); |
927 | |
928 | string[0] = string[1] = history_expansion_char; |
929 | string[2] = ':'; |
930 | string[3] = 's'; |
931 | strlcpy (string + 4, hstring, l + 1); |
932 | l += 4; |
933 | } |
934 | else |
935 | { |
936 | #if defined (HANDLE_MULTIBYTE) |
937 | memset (&ps, 0, sizeof (mbstate_t)); |
938 | #endif |
939 | |
940 | string = hstring; |
941 | /* If not quick substitution, still maybe have to do expansion. */ |
942 | |
943 | /* `!' followed by one of the characters in history_no_expand_chars |
944 | is NOT an expansion. */ |
945 | for (i = 0; string[i]; i++) |
946 | { |
947 | #if defined (HANDLE_MULTIBYTE) |
948 | if (MB_CUR_MAX1 > 1 && rl_byte_oriented == 0) |
949 | { |
950 | int v; |
951 | v = _rl_get_char_len (string + i, &ps); |
952 | if (v > 1) |
953 | { |
954 | i += v - 1; |
955 | continue; |
956 | } |
957 | } |
958 | #endif /* HANDLE_MULTIBYTE */ |
959 | |
960 | cc = string[i + 1]; |
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)((string[i - 1]) ? ((char *)strchr ((history_word_delimiters) , (string[i - 1])) != (char *)((void *)0)) : 0))) |
967 | { |
968 | while (string[i]) |
969 | i++; |
970 | break; |
971 | } |
972 | else if (string[i] == history_expansion_char) |
973 | { |
974 | if (!cc || member (cc, history_no_expand_chars)((cc) ? ((char *)strchr ((history_no_expand_chars), (cc)) != ( char *)((void *)0)) : 0)) |
975 | continue; |
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 |
980 | non-zero value. */ |
981 | else if (history_inhibit_expansion_function && |
982 | (*history_inhibit_expansion_function) (string, i)) |
983 | continue; |
984 | else |
985 | break; |
986 | } |
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] == '\'') |
990 | { |
991 | /* If this is bash, single quotes inhibit history expansion. */ |
992 | i++; |
993 | hist_string_extract_single_quoted (string, &i); |
994 | } |
995 | else if (history_quotes_inhibit_expansion && string[i] == '\\') |
996 | { |
997 | /* If this is bash, allow backslashes to quote single |
998 | quotes and the history expansion character. */ |
999 | if (cc == '\'' || cc == history_expansion_char) |
1000 | i++; |
1001 | } |
1002 | } |
1003 | |
1004 | if (string[i] != history_expansion_char) |
1005 | { |
1006 | free (result); |
1007 | *output = savestring (string)xstrdup(string); |
1008 | return (0); |
1009 | } |
1010 | } |
1011 | |
1012 | /* Extract and perform the substitution. */ |
1013 | for (passc = i = j = 0; i < l; i++) |
1014 | { |
1015 | int tchar = string[i]; |
1016 | |
1017 | if (passc) |
1018 | { |
1019 | passc = 0; |
1020 | ADD_CHAR (tchar)do { if (j >= result_len - 1) result = (char *)xrealloc (result , result_len += 64); result[j++] = tchar; result[j] = '\0'; } while (0); |
1021 | continue; |
1022 | } |
1023 | |
1024 | #if defined (HANDLE_MULTIBYTE) |
1025 | if (MB_CUR_MAX1 > 1 && rl_byte_oriented == 0) |
1026 | { |
1027 | int k, c; |
1028 | |
1029 | c = tchar; |
1030 | memset (mb, 0, sizeof (mb)); |
1031 | for (k = 0; k < MB_LEN_MAX1; k++) |
1032 | { |
1033 | mb[k] = (char)c; |
1034 | memset (&ps, 0, sizeof (mbstate_t)); |
1035 | if (_rl_get_char_len (mb, &ps) == -2) |
1036 | c = string[++i]; |
1037 | else |
1038 | break; |
1039 | } |
1040 | if (strlen (mb) > 1) |
1041 | { |
1042 | ADD_STRING (mb)do { int sl = strlen (mb); j += sl; if (j >= result_len) { while (j >= result_len) result_len += 128; result = (char *)xrealloc (result, result_len); } strlcpy (result + j - sl, mb, result_len - j + sl); } while (0); |
1043 | break; |
1044 | } |
1045 | } |
1046 | #endif /* HANDLE_MULTIBYTE */ |
1047 | |
1048 | if (tchar == history_expansion_char) |
1049 | tchar = -3; |
1050 | else if (tchar == history_comment_char) |
1051 | tchar = -2; |
1052 | |
1053 | switch (tchar) |
1054 | { |
1055 | default: |
1056 | ADD_CHAR (string[i])do { if (j >= result_len - 1) result = (char *)xrealloc (result , result_len += 64); result[j++] = string[i]; result[j] = '\0' ; } while (0); |
1057 | break; |
1058 | |
1059 | case '\\': |
1060 | passc++; |
1061 | ADD_CHAR (tchar)do { if (j >= result_len - 1) result = (char *)xrealloc (result , result_len += 64); result[j++] = tchar; result[j] = '\0'; } while (0); |
1062 | break; |
1063 | |
1064 | case '\'': |
1065 | { |
1066 | /* If history_quotes_inhibit_expansion is set, single quotes |
1067 | inhibit history expansion. */ |
1068 | if (history_quotes_inhibit_expansion) |
1069 | { |
1070 | int quote, slen; |
1071 | |
1072 | quote = i++; |
1073 | hist_string_extract_single_quoted (string, &i); |
1074 | |
1075 | slen = i - quote + 2; |
1076 | temp = (char *)xmalloc (slen); |
1077 | strncpy (temp, string + quote, slen); |
1078 | temp[slen - 1] = '\0'; |
1079 | ADD_STRING (temp)do { int sl = strlen (temp); j += sl; if (j >= result_len) { while (j >= result_len) result_len += 128; result = (char *)xrealloc (result, result_len); } strlcpy (result + j - sl, temp, result_len - j + sl); } while (0); |
1080 | free (temp); |
1081 | } |
1082 | else |
1083 | ADD_CHAR (string[i])do { if (j >= result_len - 1) result = (char *)xrealloc (result , result_len += 64); result[j++] = string[i]; result[j] = '\0' ; } while (0); |
1084 | break; |
1085 | } |
1086 | |
1087 | case -2: /* history_comment_char */ |
1088 | if (i == 0 || member (string[i - 1], history_word_delimiters)((string[i - 1]) ? ((char *)strchr ((history_word_delimiters) , (string[i - 1])) != (char *)((void *)0)) : 0)) |
1089 | { |
1090 | temp = (char *)xmalloc (l - i + 1); |
1091 | strlcpy (temp, string + i, l - i + 1); |
1092 | ADD_STRING (temp)do { int sl = strlen (temp); j += sl; if (j >= result_len) { while (j >= result_len) result_len += 128; result = (char *)xrealloc (result, result_len); } strlcpy (result + j - sl, temp, result_len - j + sl); } while (0); |
1093 | free (temp); |
1094 | i = l; |
1095 | } |
1096 | else |
1097 | ADD_CHAR (string[i])do { if (j >= result_len - 1) result = (char *)xrealloc (result , result_len += 64); result[j++] = string[i]; result[j] = '\0' ; } while (0); |
1098 | break; |
1099 | |
1100 | case -3: /* history_expansion_char */ |
1101 | cc = string[i + 1]; |
1102 | |
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)((cc) ? ((char *)strchr ((history_no_expand_chars), (cc)) != ( char *)((void *)0)) : 0)) |
1107 | { |
1108 | ADD_CHAR (string[i])do { if (j >= result_len - 1) result = (char *)xrealloc (result , result_len += 64); result[j++] = string[i]; result[j] = '\0' ; } while (0); |
1109 | break; |
1110 | } |
1111 | |
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. */ |
1118 | if (cc == '#') |
1119 | { |
1120 | if (result) |
1121 | { |
1122 | temp = (char *)xmalloc (1 + strlen (result)); |
1123 | strlcpy (temp, result, 1 + strlen(result)); |
1124 | ADD_STRING (temp)do { int sl = strlen (temp); j += sl; if (j >= result_len) { while (j >= result_len) result_len += 128; result = (char *)xrealloc (result, result_len); } strlcpy (result + j - sl, temp, result_len - j + sl); } while (0); |
1125 | free (temp); |
1126 | } |
1127 | i++; |
1128 | break; |
1129 | } |
1130 | #endif |
1131 | |
1132 | r = history_expand_internal (string, i, &eindex, &temp, result); |
1133 | if (r < 0) |
1134 | { |
1135 | *output = temp; |
1136 | free (result); |
1137 | if (string != hstring) |
1138 | free (string); |
1139 | return -1; |
1140 | } |
1141 | else |
1142 | { |
1143 | if (temp) |
1144 | { |
1145 | modified++; |
1146 | if (*temp) |
1147 | ADD_STRING (temp)do { int sl = strlen (temp); j += sl; if (j >= result_len) { while (j >= result_len) result_len += 128; result = (char *)xrealloc (result, result_len); } strlcpy (result + j - sl, temp, result_len - j + sl); } while (0); |
1148 | free (temp); |
1149 | } |
1150 | only_printing = r == 1; |
1151 | i = eindex; |
1152 | } |
1153 | break; |
1154 | } |
1155 | } |
1156 | |
1157 | *output = result; |
1158 | if (string != hstring) |
1159 | free (string); |
1160 | |
1161 | if (only_printing) |
1162 | { |
1163 | add_history (result); |
1164 | return (2); |
1165 | } |
1166 | |
1167 | return (modified != 0); |
1168 | } |
1169 | |
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. */ |
1175 | static char * |
1176 | get_history_word_specifier (spec, from, caller_index) |
1177 | char *spec, *from; |
1178 | int *caller_index; |
1179 | { |
1180 | register int i = *caller_index; |
1181 | int first, last; |
1182 | int expecting_word_spec = 0; |
1183 | char *result; |
1184 | |
1185 | /* The range of words to return doesn't exist yet. */ |
1186 | first = last = 0; |
Value stored to 'first' is never read | |
1187 | result = (char *)NULL((void *)0); |
1188 | |
1189 | /* If we found a colon, then this *must* be a word specification. If |
1190 | it isn't, then it is an error. */ |
1191 | if (spec[i] == ':') |
1192 | { |
1193 | i++; |
1194 | expecting_word_spec++; |
1195 | } |
1196 | |
1197 | /* Handle special cases first. */ |
1198 | |
1199 | /* `%' is the word last searched for. */ |
1200 | if (spec[i] == '%') |
1201 | { |
1202 | *caller_index = i + 1; |
1203 | return (search_match ? savestring (search_match)xstrdup(search_match) : savestring ("")xstrdup("")); |
1204 | } |
1205 | |
1206 | /* `*' matches all of the arguments, but not the command. */ |
1207 | if (spec[i] == '*') |
1208 | { |
1209 | *caller_index = i + 1; |
1210 | result = history_arg_extract (1, '$', from); |
1211 | return (result ? result : savestring ("")xstrdup("")); |
1212 | } |
1213 | |
1214 | /* `$' is last arg. */ |
1215 | if (spec[i] == '$') |
1216 | { |
1217 | *caller_index = i + 1; |
1218 | return (history_arg_extract ('$', '$', from)); |
1219 | } |
1220 | |
1221 | /* Try to get FIRST and LAST figured out. */ |
1222 | |
1223 | if (spec[i] == '-') |
1224 | first = 0; |
1225 | else if (spec[i] == '^') |
1226 | first = 1; |
1227 | else if (_rl_digit_p (spec[i])((spec[i]) >= '0' && (spec[i]) <= '9') && expecting_word_spec) |
1228 | { |
1229 | for (first = 0; _rl_digit_p (spec[i])((spec[i]) >= '0' && (spec[i]) <= '9'); i++) |
1230 | first = (first * 10) + _rl_digit_value (spec[i])((spec[i]) - '0'); |
1231 | } |
1232 | else |
1233 | return ((char *)NULL((void *)0)); /* no valid `first' for word specifier */ |
1234 | |
1235 | if (spec[i] == '^' || spec[i] == '*') |
1236 | { |
1237 | last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */ |
1238 | i++; |
1239 | } |
1240 | else if (spec[i] != '-') |
1241 | last = first; |
1242 | else |
1243 | { |
1244 | i++; |
1245 | |
1246 | if (_rl_digit_p (spec[i])((spec[i]) >= '0' && (spec[i]) <= '9')) |
1247 | { |
1248 | for (last = 0; _rl_digit_p (spec[i])((spec[i]) >= '0' && (spec[i]) <= '9'); i++) |
1249 | last = (last * 10) + _rl_digit_value (spec[i])((spec[i]) - '0'); |
1250 | } |
1251 | else if (spec[i] == '$') |
1252 | { |
1253 | i++; |
1254 | last = '$'; |
1255 | } |
1256 | #if 0 |
1257 | else if (!spec[i] || spec[i] == ':') |
1258 | /* check against `:' because there could be a modifier separator */ |
1259 | #else |
1260 | else |
1261 | /* csh seems to allow anything to terminate the word spec here, |
1262 | leaving it as an abbreviation. */ |
1263 | #endif |
1264 | last = -1; /* x- abbreviates x-$ omitting word `$' */ |
1265 | } |
1266 | |
1267 | *caller_index = i; |
1268 | |
1269 | if (last >= first || last == '$' || last < 0) |
1270 | result = history_arg_extract (first, last, from); |
1271 | |
1272 | return (result ? result : (char *)&error_pointer); |
1273 | } |
1274 | |
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. */ |
1280 | char * |
1281 | history_arg_extract (first, last, string) |
1282 | int first, last; |
1283 | const char *string; |
1284 | { |
1285 | register int i, len; |
1286 | char *result; |
1287 | int size, offset; |
1288 | char **list; |
1289 | |
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((void *)0)) |
1294 | return ((char *)NULL((void *)0)); |
1295 | |
1296 | for (len = 0; list[len]; len++) |
1297 | ; |
1298 | |
1299 | if (last < 0) |
1300 | last = len + last - 1; |
1301 | |
1302 | if (first < 0) |
1303 | first = len + first - 1; |
1304 | |
1305 | if (last == '$') |
1306 | last = len - 1; |
1307 | |
1308 | if (first == '$') |
1309 | first = len - 1; |
1310 | |
1311 | last++; |
1312 | |
1313 | if (first >= len || last > len || first < 0 || last < 0 || first > last) |
1314 | result = ((char *)NULL((void *)0)); |
1315 | else |
1316 | { |
1317 | for (size = 0, i = first; i < last; i++) |
1318 | size += strlen (list[i]) + 1; |
1319 | result = (char *)xmalloc (size + 1); |
1320 | result[0] = '\0'; |
1321 | |
1322 | for (i = first, offset = 0; i < last; i++) |
1323 | { |
1324 | strlcpy (result + offset, list[i], size + 1 - offset); |
1325 | offset += strlen (list[i]); |
1326 | if (i + 1 < last) |
1327 | { |
1328 | result[offset++] = ' '; |
1329 | result[offset] = 0; |
1330 | } |
1331 | } |
1332 | } |
1333 | |
1334 | for (i = 0; i < len; i++) |
1335 | free (list[i]); |
1336 | free (list); |
1337 | |
1338 | return (result); |
1339 | } |
1340 | |
1341 | #define slashify_in_quotes"\\`\"$" "\\`\"$" |
1342 | |
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 |
1346 | *INDP. */ |
1347 | static char ** |
1348 | history_tokenize_internal (string, wind, indp) |
1349 | const char *string; |
1350 | int wind, *indp; |
1351 | { |
1352 | char **result; |
1353 | register int i, start, result_index, size; |
1354 | int len, delimiter; |
1355 | |
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) |
1359 | *indp = -1; |
1360 | |
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((void *)0); string[i]; ) |
1364 | { |
1365 | delimiter = 0; |
1366 | |
1367 | /* Skip leading whitespace. */ |
1368 | for (; string[i] && whitespace (string[i])(((string[i]) == ' ') || ((string[i]) == '\t')); i++) |
1369 | ; |
1370 | if (string[i] == 0 || string[i] == history_comment_char) |
1371 | return (result); |
1372 | |
1373 | start = i; |
1374 | |
1375 | if (member (string[i], "()\n")((string[i]) ? ((char *)strchr (("()\n"), (string[i])) != (char *)((void *)0)) : 0)) |
1376 | { |
1377 | i++; |
1378 | goto got_token; |
1379 | } |
1380 | |
1381 | if (member (string[i], "<>;&|$")((string[i]) ? ((char *)strchr (("<>;&|$"), (string [i])) != (char *)((void *)0)) : 0)) |
1382 | { |
1383 | int peek = string[i + 1]; |
1384 | |
1385 | if (peek == string[i] && peek != '$') |
1386 | { |
1387 | if (peek == '<' && string[i + 2] == '-') |
1388 | i++; |
1389 | i += 2; |
1390 | goto got_token; |
1391 | } |
1392 | else |
1393 | { |
1394 | if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || |
1395 | ((peek == '>') && (string[i] == '&')) || |
1396 | ((peek == '(') && (string[i] == '$'))) |
1397 | { |
1398 | i += 2; |
1399 | goto got_token; |
1400 | } |
1401 | } |
1402 | if (string[i] != '$') |
1403 | { |
1404 | i++; |
1405 | goto got_token; |
1406 | } |
1407 | } |
1408 | |
1409 | /* Get word from string + i; */ |
1410 | |
1411 | if (member (string[i], HISTORY_QUOTE_CHARACTERS)((string[i]) ? ((char *)strchr (("\"'`"), (string[i])) != (char *)((void *)0)) : 0)) |
1412 | delimiter = string[i++]; |
1413 | |
1414 | for (; string[i]; i++) |
1415 | { |
1416 | if (string[i] == '\\' && string[i + 1] == '\n') |
1417 | { |
1418 | i++; |
1419 | continue; |
1420 | } |
1421 | |
1422 | if (string[i] == '\\' && delimiter != '\'' && |
1423 | (delimiter != '"' || member (string[i], slashify_in_quotes)((string[i]) ? ((char *)strchr (("\\`\"$"), (string[i])) != ( char *)((void *)0)) : 0))) |
1424 | { |
1425 | i++; |
1426 | continue; |
1427 | } |
1428 | |
1429 | if (delimiter && string[i] == delimiter) |
1430 | { |
1431 | delimiter = 0; |
1432 | continue; |
1433 | } |
1434 | |
1435 | if (!delimiter && (member (string[i], history_word_delimiters)((string[i]) ? ((char *)strchr ((history_word_delimiters), (string [i])) != (char *)((void *)0)) : 0))) |
1436 | break; |
1437 | |
1438 | if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS)((string[i]) ? ((char *)strchr (("\"'`"), (string[i])) != (char *)((void *)0)) : 0)) |
1439 | delimiter = string[i]; |
1440 | } |
1441 | |
1442 | got_token: |
1443 | |
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; |
1448 | |
1449 | len = i - start; |
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((void *)0); |
1456 | } |
1457 | |
1458 | return (result); |
1459 | } |
1460 | |
1461 | /* Return an array of tokens, much as the shell might. The tokens are |
1462 | parsed out of STRING. */ |
1463 | char ** |
1464 | history_tokenize (string) |
1465 | const char *string; |
1466 | { |
1467 | return (history_tokenize_internal (string, -1, (int *)NULL((void *)0))); |
1468 | } |
1469 | |
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. */ |
1473 | static char * |
1474 | history_find_word (line, ind) |
1475 | char *line; |
1476 | int ind; |
1477 | { |
1478 | char **words, *s; |
1479 | int i, wind; |
1480 | |
1481 | words = history_tokenize_internal (line, ind, &wind); |
1482 | if (wind == -1 || words == 0) |
1483 | return ((char *)NULL((void *)0)); |
1484 | s = words[wind]; |
1485 | for (i = 0; i < wind; i++) |
1486 | free (words[i]); |
1487 | for (i = wind + 1; words[i]; i++) |
1488 | free (words[i]); |
1489 | free (words); |
1490 | return s; |
1491 | } |