source: trunk/third/readline/histexpand.c @ 12992

Revision 12992, 32.5 KB checked in by kcr, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12991, which included commits to RCS files with non-trunk default branches.
Line 
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 1, 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   675 Mass Ave, Cambridge, MA 02139, USA. */
22
23#define READLINE_LIBRARY
24
25#if defined (HAVE_CONFIG_H)
26#  include <config.h>
27#endif
28
29#include <stdio.h>
30
31#if defined (HAVE_STDLIB_H)
32#  include <stdlib.h>
33#else
34#  include "ansi_stdlib.h"
35#endif /* HAVE_STDLIB_H */
36
37#if defined (HAVE_UNISTD_H)
38#  ifndef _MINIX
39#    include <sys/types.h>
40#  endif
41#  include <unistd.h>
42#endif
43
44#if defined (HAVE_STRING_H)
45#  include <string.h>
46#else
47#  include <strings.h>
48#endif /* !HAVE_STRING_H */
49
50#include "history.h"
51#include "histlib.h"
52
53#define HISTORY_WORD_DELIMITERS         " \t\n;&()|<>"
54#define HISTORY_QUOTE_CHARACTERS        "\"'`"
55
56static char error_pointer;
57
58static char *subst_lhs;
59static char *subst_rhs;
60static int subst_lhs_len;
61static int subst_rhs_len;
62
63static char *get_history_word_specifier ();
64static char *history_find_word ();
65
66extern int history_offset;
67
68extern char *single_quote ();
69static char *quote_breaks ();
70
71extern char *xmalloc (), *xrealloc ();
72
73/* Variables exported by this file. */
74/* The character that represents the start of a history expansion
75   request.  This is usually `!'. */
76char history_expansion_char = '!';
77
78/* The character that invokes word substitution if found at the start of
79   a line.  This is usually `^'. */
80char history_subst_char = '^';
81
82/* During tokenization, if this character is seen as the first character
83   of a word, then it, and all subsequent characters upto a newline are
84   ignored.  For a Bourne shell, this should be '#'.  Bash special cases
85   the interactive comment character to not be a comment delimiter. */
86char history_comment_char = '\0';
87
88/* The list of characters which inhibit the expansion of text if found
89   immediately following history_expansion_char. */
90char *history_no_expand_chars = " \t\n\r=";
91
92/* If set to a non-zero value, single quotes inhibit history expansion.
93   The default is 0. */
94int history_quotes_inhibit_expansion = 0;
95
96/* If set, this points to a function that is called to verify that a
97   particular history expansion should be performed. */
98Function *history_inhibit_expansion_function;
99
100/* **************************************************************** */
101/*                                                                  */
102/*                      History Expansion                           */
103/*                                                                  */
104/* **************************************************************** */
105
106/* Hairy history expansion on text, not tokens.  This is of general
107   use, and thus belongs in this library. */
108
109/* The last string searched for by a !?string? search. */
110static char *search_string;
111
112/* The last string matched by a !?string? search. */
113static char *search_match;
114
115/* Return the event specified at TEXT + OFFSET modifying OFFSET to
116   point to after the event specifier.  Just a pointer to the history
117   line is returned; NULL is returned in the event of a bad specifier.
118   You pass STRING with *INDEX equal to the history_expansion_char that
119   begins this specification.
120   DELIMITING_QUOTE is a character that is allowed to end the string
121   specification for what to search for in addition to the normal
122   characters `:', ` ', `\t', `\n', and sometimes `?'.
123   So you might call this function like:
124   line = get_history_event ("!echo:p", &index, 0);  */
125char *
126get_history_event (string, caller_index, delimiting_quote)
127     char *string;
128     int *caller_index;
129     int delimiting_quote;
130{
131  register int i;
132  register char c;
133  HIST_ENTRY *entry;
134  int which, sign, local_index, substring_okay;
135  Function *search_func;
136  char *temp;
137
138  /* The event can be specified in a number of ways.
139
140     !!   the previous command
141     !n   command line N
142     !-n  current command-line minus N
143     !str the most recent command starting with STR
144     !?str[?]
145          the most recent command containing STR
146
147     All values N are determined via HISTORY_BASE. */
148
149  i = *caller_index;
150
151  if (string[i] != history_expansion_char)
152    return ((char *)NULL);
153
154  /* Move on to the specification. */
155  i++;
156
157  sign = 1;
158  substring_okay = 0;
159
160#define RETURN_ENTRY(e, w) \
161        return ((e = history_get (w)) ? e->line : (char *)NULL)
162
163  /* Handle !! case. */
164  if (string[i] == history_expansion_char)
165    {
166      i++;
167      which = history_base + (history_length - 1);
168      *caller_index = i;
169      RETURN_ENTRY (entry, which);
170    }
171
172  /* Hack case of numeric line specification. */
173  if (string[i] == '-')
174    {
175      sign = -1;
176      i++;
177    }
178
179  if (_rl_digit_p (string[i]))
180    {
181      /* Get the extent of the digits and compute the value. */
182      for (which = 0; _rl_digit_p (string[i]); i++)
183        which = (which * 10) + _rl_digit_value (string[i]);
184
185      *caller_index = i;
186
187      if (sign < 0)
188        which = (history_length + history_base) - which;
189
190      RETURN_ENTRY (entry, which);
191    }
192
193  /* This must be something to search for.  If the spec begins with
194     a '?', then the string may be anywhere on the line.  Otherwise,
195     the string must be found at the start of a line. */
196  if (string[i] == '?')
197    {
198      substring_okay++;
199      i++;
200    }
201
202  /* Only a closing `?' or a newline delimit a substring search string. */
203  for (local_index = i; c = string[i]; i++)
204    if ((!substring_okay && (whitespace (c) || c == ':' ||
205        (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
206        string[i] == delimiting_quote)) ||
207        string[i] == '\n' ||
208        (substring_okay && string[i] == '?'))
209      break;
210
211  which = i - local_index;
212  temp = xmalloc (1 + which);
213  if (which)
214    strncpy (temp, string + local_index, which);
215  temp[which] = '\0';
216
217  if (substring_okay && string[i] == '?')
218    i++;
219
220  *caller_index = i;
221
222#define FAIL_SEARCH() \
223  do { \
224    history_offset = history_length; free (temp) ; return (char *)NULL; \
225  } while (0)
226
227  /* If there is no search string, try to use the previous search string,
228     if one exists.  If not, fail immediately. */
229  if (*temp == '\0' && substring_okay)
230    {
231      if (search_string)
232        {
233          free (temp);
234          temp = savestring (search_string);
235        }
236      else
237        FAIL_SEARCH ();
238    }
239
240  search_func = substring_okay ? history_search : history_search_prefix;
241  while (1)
242    {
243      local_index = (*search_func) (temp, -1);
244
245      if (local_index < 0)
246        FAIL_SEARCH ();
247
248      if (local_index == 0 || substring_okay)
249        {
250          entry = current_history ();
251          history_offset = history_length;
252       
253          /* If this was a substring search, then remember the
254             string that we matched for word substitution. */
255          if (substring_okay)
256            {
257              FREE (search_string);
258              search_string = temp;
259
260              FREE (search_match);
261              search_match = history_find_word (entry->line, local_index);
262            }
263          else
264            free (temp);
265
266          return (entry->line);
267        }
268
269      if (history_offset)
270        history_offset--;
271      else
272        FAIL_SEARCH ();
273    }
274#undef FAIL_SEARCH
275#undef RETURN_ENTRY
276}
277
278/* Function for extracting single-quoted strings.  Used for inhibiting
279   history expansion within single quotes. */
280
281/* Extract the contents of STRING as if it is enclosed in single quotes.
282   SINDEX, when passed in, is the offset of the character immediately
283   following the opening single quote; on exit, SINDEX is left pointing
284   to the closing single quote. */
285static void
286hist_string_extract_single_quoted (string, sindex)
287     char *string;
288     int *sindex;
289{
290  register int i;
291
292  for (i = *sindex; string[i] && string[i] != '\''; i++)
293    ;
294
295  *sindex = i;
296}
297
298static char *
299quote_breaks (s)
300     char *s;
301{
302  register char *p, *r;
303  char *ret;
304  int len = 3;
305
306  for (p = s; p && *p; p++, len++)
307    {
308      if (*p == '\'')
309        len += 3;
310      else if (whitespace (*p) || *p == '\n')
311        len += 2;
312    }
313
314  r = ret = xmalloc (len);
315  *r++ = '\'';
316  for (p = s; p && *p; )
317    {
318      if (*p == '\'')
319        {
320          *r++ = '\'';
321          *r++ = '\\';
322          *r++ = '\'';
323          *r++ = '\'';
324          p++;
325        }
326      else if (whitespace (*p) || *p == '\n')
327        {
328          *r++ = '\'';
329          *r++ = *p++;
330          *r++ = '\'';
331        }
332      else
333        *r++ = *p++;
334    }
335  *r++ = '\'';
336  *r = '\0';
337  return ret;
338}
339
340static char *
341hist_error(s, start, current, errtype)
342      char *s;
343      int start, current, errtype;
344{
345  char *temp, *emsg;
346  int ll, elen;
347
348  ll = current - start;
349
350  switch (errtype)
351    {
352    case EVENT_NOT_FOUND:
353      emsg = "event not found";
354      elen = 15;
355      break;
356    case BAD_WORD_SPEC:
357      emsg = "bad word specifier";
358      elen = 18;
359      break;
360    case SUBST_FAILED:
361      emsg = "substitution failed";
362      elen = 19;
363      break;
364    case BAD_MODIFIER:
365      emsg = "unrecognized history modifier";
366      elen = 29;
367      break;
368    case NO_PREV_SUBST:
369      emsg = "no previous substitution";
370      elen = 24;
371      break;
372    default:
373      emsg = "unknown expansion error";
374      elen = 23;
375      break;
376    }
377
378  temp = xmalloc (ll + elen + 3);
379  strncpy (temp, s + start, ll);
380  temp[ll] = ':';
381  temp[ll + 1] = ' ';
382  strcpy (temp + ll + 2, emsg);
383  return (temp);
384}
385
386/* Get a history substitution string from STR starting at *IPTR
387   and return it.  The length is returned in LENPTR.
388
389   A backslash can quote the delimiter.  If the string is the
390   empty string, the previous pattern is used.  If there is
391   no previous pattern for the lhs, the last history search
392   string is used.
393
394   If IS_RHS is 1, we ignore empty strings and set the pattern
395   to "" anyway.  subst_lhs is not changed if the lhs is empty;
396   subst_rhs is allowed to be set to the empty string. */
397
398static char *
399get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
400     char *str;
401     int *iptr, delimiter, is_rhs, *lenptr;
402{
403  register int si, i, j, k;
404  char *s = (char *) NULL;
405
406  i = *iptr;
407
408  for (si = i; str[si] && str[si] != delimiter; si++)
409    if (str[si] == '\\' && str[si + 1] == delimiter)
410      si++;
411
412  if (si > i || is_rhs)
413    {
414      s = xmalloc (si - i + 1);
415      for (j = 0, k = i; k < si; j++, k++)
416        {
417          /* Remove a backslash quoting the search string delimiter. */
418          if (str[k] == '\\' && str[k + 1] == delimiter)
419            k++;
420          s[j] = str[k];
421        }
422      s[j] = '\0';
423      if (lenptr)
424        *lenptr = j;
425    }
426
427  i = si;
428  if (str[i])
429    i++;
430  *iptr = i;
431
432  return s;
433}
434
435static void
436postproc_subst_rhs ()
437{
438  char *new;
439  int i, j, new_size;
440
441  new = xmalloc (new_size = subst_rhs_len + subst_lhs_len);
442  for (i = j = 0; i < subst_rhs_len; i++)
443    {
444      if (subst_rhs[i] == '&')
445        {
446          if (j + subst_lhs_len >= new_size)
447            new = xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
448          strcpy (new + j, subst_lhs);
449          j += subst_lhs_len;
450        }
451      else
452        {
453          /* a single backslash protects the `&' from lhs interpolation */
454          if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
455            i++;
456          if (j >= new_size)
457            new = xrealloc (new, new_size *= 2);
458          new[j++] = subst_rhs[i];
459        }
460    }
461  new[j] = '\0';
462  free (subst_rhs);
463  subst_rhs = new;
464  subst_rhs_len = j;
465}
466
467/* Expand the bulk of a history specifier starting at STRING[START].
468   Returns 0 if everything is OK, -1 if an error occurred, and 1
469   if the `p' modifier was supplied and the caller should just print
470   the returned string.  Returns the new index into string in
471   *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
472static int
473history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
474     char *string;
475     int start, *end_index_ptr;
476     char **ret_string;
477     char *current_line;        /* for !# */
478{
479  int i, n, starting_index;
480  int substitute_globally, want_quotes, print_only;
481  char *event, *temp, *result, *tstr, *t, c, *word_spec;
482  int result_len;
483
484  result = xmalloc (result_len = 128);
485
486  i = start;
487
488  /* If it is followed by something that starts a word specifier,
489     then !! is implied as the event specifier. */
490
491  if (member (string[i + 1], ":$*%^"))
492    {
493      char fake_s[3];
494      int fake_i = 0;
495      i++;
496      fake_s[0] = fake_s[1] = history_expansion_char;
497      fake_s[2] = '\0';
498      event = get_history_event (fake_s, &fake_i, 0);
499    }
500  else if (string[i + 1] == '#')
501    {
502      i += 2;
503      event = current_line;
504    }
505  else
506    {
507      int quoted_search_delimiter = 0;
508
509      /* If the character before this `!' is a double or single
510         quote, then this expansion takes place inside of the
511         quoted string.  If we have to search for some text ("!foo"),
512         allow the delimiter to end the search string. */
513      if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
514        quoted_search_delimiter = string[i - 1];
515      event = get_history_event (string, &i, quoted_search_delimiter);
516    }
517         
518  if (event == 0)
519    {
520      *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
521      free (result);
522      return (-1);
523    }
524
525  /* If a word specifier is found, then do what that requires. */
526  starting_index = i;
527  word_spec = get_history_word_specifier (string, event, &i);
528
529  /* There is no such thing as a `malformed word specifier'.  However,
530     it is possible for a specifier that has no match.  In that case,
531     we complain. */
532  if (word_spec == (char *)&error_pointer)
533    {
534      *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
535      free (result);
536      return (-1);
537    }
538
539  /* If no word specifier, than the thing of interest was the event. */
540  temp = word_spec ? savestring (word_spec) : savestring (event);
541  FREE (word_spec);
542
543  /* Perhaps there are other modifiers involved.  Do what they say. */
544  want_quotes = substitute_globally = print_only = 0;
545  starting_index = i;
546
547  while (string[i] == ':')
548    {
549      c = string[i + 1];
550
551      if (c == 'g')
552        {
553          substitute_globally = 1;
554          i++;
555          c = string[i + 1];
556        }
557
558      switch (c)
559        {
560        default:
561          *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
562          free (result);
563          free (temp);
564          return -1;
565
566        case 'q':
567          want_quotes = 'q';
568          break;
569
570        case 'x':
571          want_quotes = 'x';
572          break;
573
574          /* :p means make this the last executed line.  So we
575             return an error state after adding this line to the
576             history. */
577        case 'p':
578          print_only++;
579          break;
580
581          /* :t discards all but the last part of the pathname. */
582        case 't':
583          tstr = strrchr (temp, '/');
584          if (tstr)
585            {
586              tstr++;
587              t = savestring (tstr);
588              free (temp);
589              temp = t;
590            }
591          break;
592
593          /* :h discards the last part of a pathname. */
594        case 'h':
595          tstr = strrchr (temp, '/');
596          if (tstr)
597            *tstr = '\0';
598          break;
599
600          /* :r discards the suffix. */
601        case 'r':
602          tstr = strrchr (temp, '.');
603          if (tstr)
604            *tstr = '\0';
605          break;
606
607          /* :e discards everything but the suffix. */
608        case 'e':
609          tstr = strrchr (temp, '.');
610          if (tstr)
611            {
612              t = savestring (tstr);
613              free (temp);
614              temp = t;
615            }
616          break;
617
618        /* :s/this/that substitutes `that' for the first
619           occurrence of `this'.  :gs/this/that substitutes `that'
620           for each occurrence of `this'.  :& repeats the last
621           substitution.  :g& repeats the last substitution
622           globally. */
623
624        case '&':
625        case 's':
626          {
627            char *new_event, *t;
628            int delimiter, failed, si, l_temp;
629
630            if (c == 's')
631              {
632                if (i + 2 < (int)strlen (string))
633                  delimiter = string[i + 2];
634                else
635                  break;        /* no search delimiter */
636
637                i += 3;
638
639                t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
640                /* An empty substitution lhs with no previous substitution
641                   uses the last search string as the lhs. */
642                if (t)
643                  {
644                    FREE (subst_lhs);
645                    subst_lhs = t;
646                  }
647                else if (!subst_lhs)
648                  {
649                    if (search_string && *search_string)
650                      {
651                        subst_lhs = savestring (search_string);
652                        subst_lhs_len = strlen (subst_lhs);
653                      }
654                    else
655                      {
656                        subst_lhs = (char *) NULL;
657                        subst_lhs_len = 0;
658                      }
659                  }
660
661                FREE (subst_rhs);
662                subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);
663
664                /* If `&' appears in the rhs, it's supposed to be replaced
665                   with the lhs. */
666                if (member ('&', subst_rhs))
667                  postproc_subst_rhs ();
668              }
669            else
670              i += 2;
671
672            /* If there is no lhs, the substitution can't succeed. */
673            if (subst_lhs_len == 0)
674              {
675                *ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
676                free (result);
677                free (temp);
678                return -1;
679              }
680
681            l_temp = strlen (temp);
682            /* Ignore impossible cases. */
683            if (subst_lhs_len > l_temp)
684              {
685                *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
686                free (result);
687                free (temp);
688                return (-1);
689              }
690
691            /* Find the first occurrence of THIS in TEMP. */
692            si = 0;
693            for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
694              if (STREQN (temp+si, subst_lhs, subst_lhs_len))
695                {
696                  int len = subst_rhs_len - subst_lhs_len + l_temp;
697                  new_event = xmalloc (1 + len);
698                  strncpy (new_event, temp, si);
699                  strncpy (new_event + si, subst_rhs, subst_rhs_len);
700                  strncpy (new_event + si + subst_rhs_len,
701                           temp + si + subst_lhs_len,
702                           l_temp - (si + subst_lhs_len));
703                  new_event[len] = '\0';
704                  free (temp);
705                  temp = new_event;
706
707                  failed = 0;
708
709                  if (substitute_globally)
710                    {
711                      si += subst_rhs_len;
712                      l_temp = strlen (temp);
713                      substitute_globally++;
714                      continue;
715                    }
716                  else
717                    break;
718                }
719
720            if (substitute_globally > 1)
721              {
722                substitute_globally = 0;
723                continue;       /* don't want to increment i */
724              }
725
726            if (failed == 0)
727              continue;         /* don't want to increment i */
728
729            *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
730            free (result);
731            free (temp);
732            return (-1);
733          }
734        }
735      i += 2;
736    }
737  /* Done with modfiers. */
738  /* Believe it or not, we have to back the pointer up by one. */
739  --i;
740
741  if (want_quotes)
742    {
743      char *x;
744
745      if (want_quotes == 'q')
746        x = single_quote (temp);
747      else if (want_quotes == 'x')
748        x = quote_breaks (temp);
749      else
750        x = savestring (temp);
751
752      free (temp);
753      temp = x;
754    }
755
756  n = strlen (temp);
757  if (n >= result_len)
758    result = xrealloc (result, n + 2);
759  strcpy (result, temp);
760  free (temp);
761
762  *end_index_ptr = i;
763  *ret_string = result;
764  return (print_only);
765}
766
767/* Expand the string STRING, placing the result into OUTPUT, a pointer
768   to a string.  Returns:
769
770  -1) If there was an error in expansion.
771   0) If no expansions took place (or, if the only change in
772      the text was the de-slashifying of the history expansion
773      character)
774   1) If expansions did take place
775   2) If the `p' modifier was given and the caller should print the result
776
777  If an error ocurred in expansion, then OUTPUT contains a descriptive
778  error message. */
779
780#define ADD_STRING(s) \
781        do \
782          { \
783            int sl = strlen (s); \
784            j += sl; \
785            if (j >= result_len) \
786              { \
787                while (j >= result_len) \
788                  result_len += 128; \
789                result = xrealloc (result, result_len); \
790              } \
791            strcpy (result + j - sl, s); \
792          } \
793        while (0)
794
795#define ADD_CHAR(c) \
796        do \
797          { \
798            if (j >= result_len - 1) \
799              result = xrealloc (result, result_len += 64); \
800            result[j++] = c; \
801            result[j] = '\0'; \
802          } \
803        while (0)
804
805int
806history_expand (hstring, output)
807     char *hstring;
808     char **output;
809{
810  register int j;
811  int i, r, l, passc, cc, modified, eindex, only_printing;
812  char *string;
813
814  /* The output string, and its length. */
815  int result_len;
816  char *result;
817
818  /* Used when adding the string. */
819  char *temp;
820
821  /* Setting the history expansion character to 0 inhibits all
822     history expansion. */
823  if (history_expansion_char == 0)
824    {
825      *output = savestring (hstring);
826      return (0);
827    }
828   
829  /* Prepare the buffer for printing error messages. */
830  result = xmalloc (result_len = 256);
831  result[0] = '\0';
832
833  only_printing = modified = 0;
834  l = strlen (hstring);
835
836  /* Grovel the string.  Only backslash and single quotes can quote the
837     history escape character.  We also handle arg specifiers. */
838
839  /* Before we grovel forever, see if the history_expansion_char appears
840     anywhere within the text. */
841
842  /* The quick substitution character is a history expansion all right.  That
843     is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
844     that is the substitution that we do. */
845  if (hstring[0] == history_subst_char)
846    {
847      string = xmalloc (l + 5);
848
849      string[0] = string[1] = history_expansion_char;
850      string[2] = ':';
851      string[3] = 's';
852      strcpy (string + 4, hstring);
853      l += 4;
854    }
855  else
856    {
857      string = hstring;
858      /* If not quick substitution, still maybe have to do expansion. */
859
860      /* `!' followed by one of the characters in history_no_expand_chars
861         is NOT an expansion. */
862      for (i = 0; string[i]; i++)
863        {
864          cc = string[i + 1];
865          /* The history_comment_char, if set, appearing that the beginning
866             of a word signifies that the rest of the line should not have
867             history expansion performed on it.
868             Skip the rest of the line and break out of the loop. */
869          if (history_comment_char && string[i] == history_comment_char &&
870              (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS)))
871            {
872              while (string[i])
873                i++;
874              break;
875            }
876          else if (string[i] == history_expansion_char)
877            {
878              if (!cc || member (cc, history_no_expand_chars))
879                continue;
880              /* If the calling application has set
881                 history_inhibit_expansion_function to a function that checks
882                 for special cases that should not be history expanded,
883                 call the function and skip the expansion if it returns a
884                 non-zero value. */
885              else if (history_inhibit_expansion_function &&
886                        (*history_inhibit_expansion_function) (string, i))
887                continue;
888              else
889                break;
890            }
891          /* XXX - at some point, might want to extend this to handle
892                   double quotes as well. */
893          else if (history_quotes_inhibit_expansion && string[i] == '\'')
894            {
895              /* If this is bash, single quotes inhibit history expansion. */
896              i++;
897              hist_string_extract_single_quoted (string, &i);
898            }
899          else if (history_quotes_inhibit_expansion && string[i] == '\\')
900            {
901              /* If this is bash, allow backslashes to quote single
902                 quotes and the history expansion character. */
903              if (cc == '\'' || cc == history_expansion_char)
904                i++;
905            }
906        }
907         
908      if (string[i] != history_expansion_char)
909        {
910          free (result);
911          *output = savestring (string);
912          return (0);
913        }
914    }
915
916  /* Extract and perform the substitution. */
917  for (passc = i = j = 0; i < l; i++)
918    {
919      int tchar = string[i];
920
921      if (passc)
922        {
923          passc = 0;
924          ADD_CHAR (tchar);
925          continue;
926        }
927
928      if (tchar == history_expansion_char)
929        tchar = -3;
930      else if (tchar == history_comment_char)
931        tchar = -2;
932
933      switch (tchar)
934        {
935        default:
936          ADD_CHAR (string[i]);
937          break;
938
939        case '\\':
940          passc++;
941          ADD_CHAR (tchar);
942          break;
943
944        case '\'':
945          {
946            /* If history_quotes_inhibit_expansion is set, single quotes
947               inhibit history expansion. */
948            if (history_quotes_inhibit_expansion)
949              {
950                int quote, slen;
951
952                quote = i++;
953                hist_string_extract_single_quoted (string, &i);
954
955                slen = i - quote + 2;
956                temp = xmalloc (slen);
957                strncpy (temp, string + quote, slen);
958                temp[slen - 1] = '\0';
959                ADD_STRING (temp);
960                free (temp);
961              }
962            else
963              ADD_CHAR (string[i]);
964            break;
965          }
966
967        case -2:                /* history_comment_char */
968          if (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS))
969            {
970              temp = xmalloc (l - i + 1);
971              strcpy (temp, string + i);
972              ADD_STRING (temp);
973              free (temp);
974              i = l;
975            }
976          else
977            ADD_CHAR (string[i]);
978          break;
979
980        case -3:                /* history_expansion_char */
981          cc = string[i + 1];
982
983          /* If the history_expansion_char is followed by one of the
984             characters in history_no_expand_chars, then it is not a
985             candidate for expansion of any kind. */
986          if (member (cc, history_no_expand_chars))
987            {
988              ADD_CHAR (string[i]);
989              break;
990            }
991
992#if defined (NO_BANG_HASH_MODIFIERS)
993          /* There is something that is listed as a `word specifier' in csh
994             documentation which means `the expanded text to this point'.
995             That is not a word specifier, it is an event specifier.  If we
996             don't want to allow modifiers with `!#', just stick the current
997             output line in again. */
998          if (cc == '#')
999            {
1000              if (result)
1001                {
1002                  temp = xmalloc (1 + strlen (result));
1003                  strcpy (temp, result);
1004                  ADD_STRING (temp);
1005                  free (temp);
1006                }
1007              i++;
1008              break;
1009            }
1010#endif
1011
1012          r = history_expand_internal (string, i, &eindex, &temp, result);
1013          if (r < 0)
1014            {
1015              *output = temp;
1016              free (result);
1017              if (string != hstring)
1018                free (string);
1019              return -1;
1020            }
1021          else
1022            {
1023              if (temp)
1024                {
1025                  modified++;
1026                  if (*temp)
1027                    ADD_STRING (temp);
1028                  free (temp);
1029                }
1030              only_printing = r == 1;
1031              i = eindex;
1032            }
1033          break;
1034        }
1035    }
1036
1037  *output = result;
1038  if (string != hstring)
1039    free (string);
1040
1041  if (only_printing)
1042    {
1043      add_history (result);
1044      return (2);
1045    }
1046
1047  return (modified != 0);
1048}
1049
1050/* Return a consed string which is the word specified in SPEC, and found
1051   in FROM.  NULL is returned if there is no spec.  The address of
1052   ERROR_POINTER is returned if the word specified cannot be found.
1053   CALLER_INDEX is the offset in SPEC to start looking; it is updated
1054   to point to just after the last character parsed. */
1055static char *
1056get_history_word_specifier (spec, from, caller_index)
1057     char *spec, *from;
1058     int *caller_index;
1059{
1060  register int i = *caller_index;
1061  int first, last;
1062  int expecting_word_spec = 0;
1063  char *result;
1064
1065  /* The range of words to return doesn't exist yet. */
1066  first = last = 0;
1067  result = (char *)NULL;
1068
1069  /* If we found a colon, then this *must* be a word specification.  If
1070     it isn't, then it is an error. */
1071  if (spec[i] == ':')
1072    {
1073      i++;
1074      expecting_word_spec++;
1075    }
1076
1077  /* Handle special cases first. */
1078
1079  /* `%' is the word last searched for. */
1080  if (spec[i] == '%')
1081    {
1082      *caller_index = i + 1;
1083      return (search_match ? savestring (search_match) : savestring (""));
1084    }
1085
1086  /* `*' matches all of the arguments, but not the command. */
1087  if (spec[i] == '*')
1088    {
1089      *caller_index = i + 1;
1090      result = history_arg_extract (1, '$', from);
1091      return (result ? result : savestring (""));
1092    }
1093
1094  /* `$' is last arg. */
1095  if (spec[i] == '$')
1096    {
1097      *caller_index = i + 1;
1098      return (history_arg_extract ('$', '$', from));
1099    }
1100
1101  /* Try to get FIRST and LAST figured out. */
1102
1103  if (spec[i] == '-')
1104    first = 0;
1105  else if (spec[i] == '^')
1106    first = 1;
1107  else if (_rl_digit_p (spec[i]) && expecting_word_spec)
1108    {
1109      for (first = 0; _rl_digit_p (spec[i]); i++)
1110        first = (first * 10) + _rl_digit_value (spec[i]);
1111    }
1112  else
1113    return ((char *)NULL);      /* no valid `first' for word specifier */
1114
1115  if (spec[i] == '^' || spec[i] == '*')
1116    {
1117      last = (spec[i] == '^') ? 1 : '$';        /* x* abbreviates x-$ */
1118      i++;
1119    }
1120  else if (spec[i] != '-')
1121    last = first;
1122  else
1123    {
1124      i++;
1125
1126      if (_rl_digit_p (spec[i]))
1127        {
1128          for (last = 0; _rl_digit_p (spec[i]); i++)
1129            last = (last * 10) + _rl_digit_value (spec[i]);
1130        }
1131      else if (spec[i] == '$')
1132        {
1133          i++;
1134          last = '$';
1135        }
1136      else if (!spec[i] || spec[i] == ':')  /* could be modifier separator */
1137        last = -1;              /* x- abbreviates x-$ omitting word `$' */
1138    }
1139
1140  *caller_index = i;
1141
1142  if (last >= first || last == '$' || last < 0)
1143    result = history_arg_extract (first, last, from);
1144
1145  return (result ? result : (char *)&error_pointer);
1146}
1147
1148/* Extract the args specified, starting at FIRST, and ending at LAST.
1149   The args are taken from STRING.  If either FIRST or LAST is < 0,
1150   then make that arg count from the right (subtract from the number of
1151   tokens, so that FIRST = -1 means the next to last token on the line).
1152   If LAST is `$' the last arg from STRING is used. */
1153char *
1154history_arg_extract (first, last, string)
1155     int first, last;
1156     char *string;
1157{
1158  register int i, len;
1159  char *result;
1160  int size, offset;
1161  char **list;
1162
1163  /* XXX - think about making history_tokenize return a struct array,
1164     each struct in array being a string and a length to avoid the
1165     calls to strlen below. */
1166  if ((list = history_tokenize (string)) == NULL)
1167    return ((char *)NULL);
1168
1169  for (len = 0; list[len]; len++)
1170    ;
1171
1172  if (last < 0)
1173    last = len + last - 1;
1174
1175  if (first < 0)
1176    first = len + first - 1;
1177
1178  if (last == '$')
1179    last = len - 1;
1180
1181  if (first == '$')
1182    first = len - 1;
1183
1184  last++;
1185
1186  if (first >= len || last > len || first < 0 || last < 0 || first > last)
1187    result = ((char *)NULL);
1188  else
1189    {
1190      for (size = 0, i = first; i < last; i++)
1191        size += strlen (list[i]) + 1;
1192      result = xmalloc (size + 1);
1193      result[0] = '\0';
1194
1195      for (i = first, offset = 0; i < last; i++)
1196        {
1197          strcpy (result + offset, list[i]);
1198          offset += strlen (list[i]);
1199          if (i + 1 < last)
1200            {
1201              result[offset++] = ' ';
1202              result[offset] = 0;
1203            }
1204        }
1205    }
1206
1207  for (i = 0; i < len; i++)
1208    free (list[i]);
1209  free (list);
1210
1211  return (result);
1212}
1213
1214#define slashify_in_quotes "\\`\"$"
1215
1216/* Parse STRING into tokens and return an array of strings.  If WIND is
1217   not -1 and INDP is not null, we also want the word surrounding index
1218   WIND.  The position in the returned array of strings is returned in
1219   *INDP. */
1220static char **
1221history_tokenize_internal (string, wind, indp)
1222     char *string;
1223     int wind, *indp;
1224{
1225  char **result;
1226  register int i, start, result_index, size;
1227  int len, delimiter;
1228
1229  /* Get a token, and stuff it into RESULT.  The tokens are split
1230     exactly where the shell would split them. */
1231  for (i = result_index = size = 0, result = (char **)NULL; string[i]; )
1232    {
1233      delimiter = 0;
1234
1235      /* Skip leading whitespace. */
1236      for (; string[i] && whitespace (string[i]); i++)
1237        ;
1238      if (string[i] == 0 || string[i] == history_comment_char)
1239        return (result);
1240
1241      start = i;
1242     
1243      if (member (string[i], "()\n"))
1244        {
1245          i++;
1246          goto got_token;
1247        }
1248
1249      if (member (string[i], "<>;&|$"))
1250        {
1251          int peek = string[i + 1];
1252
1253          if (peek == string[i] && peek != '$')
1254            {
1255              if (peek == '<' && string[i + 2] == '-')
1256                i++;
1257              i += 2;
1258              goto got_token;
1259            }
1260          else
1261            {
1262              if ((peek == '&' && (string[i] == '>' || string[i] == '<')) ||
1263                  ((peek == '>') && (string[i] == '&')) ||
1264                  ((peek == '(') && (string[i] == '$')))
1265                {
1266                  i += 2;
1267                  goto got_token;
1268                }
1269            }
1270          if (string[i] != '$')
1271            {
1272              i++;
1273              goto got_token;
1274            }
1275        }
1276
1277      /* Get word from string + i; */
1278
1279      if (member (string[i], HISTORY_QUOTE_CHARACTERS))
1280        delimiter = string[i++];
1281
1282      for (; string[i]; i++)
1283        {
1284          if (string[i] == '\\' && string[i + 1] == '\n')
1285            {
1286              i++;
1287              continue;
1288            }
1289
1290          if (string[i] == '\\' && delimiter != '\'' &&
1291              (delimiter != '"' || member (string[i], slashify_in_quotes)))
1292            {
1293              i++;
1294              continue;
1295            }
1296
1297          if (delimiter && string[i] == delimiter)
1298            {
1299              delimiter = 0;
1300              continue;
1301            }
1302
1303          if (!delimiter && (member (string[i], HISTORY_WORD_DELIMITERS)))
1304            break;
1305
1306          if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
1307            delimiter = string[i];
1308        }
1309
1310    got_token:
1311
1312      /* If we are looking for the word in which the character at a
1313         particular index falls, remember it. */
1314      if (indp && wind != -1 && wind >= start && wind < i)
1315        *indp = result_index;
1316
1317      len = i - start;
1318      if (result_index + 2 >= size)
1319        result = (char **)xrealloc (result, ((size += 10) * sizeof (char *)));
1320      result[result_index] = xmalloc (1 + len);
1321      strncpy (result[result_index], string + start, len);
1322      result[result_index][len] = '\0';
1323      result[++result_index] = (char *)NULL;
1324    }
1325
1326  return (result);
1327}
1328
1329/* Return an array of tokens, much as the shell might.  The tokens are
1330   parsed out of STRING. */
1331char **
1332history_tokenize (string)
1333     char *string;
1334{
1335  return (history_tokenize_internal (string, -1, (int *)NULL));
1336}
1337
1338/* Find and return the word which contains the character at index IND
1339   in the history line LINE.  Used to save the word matched by the
1340   last history !?string? search. */
1341static char *
1342history_find_word (line, ind)
1343     char *line;
1344     int ind;
1345{
1346  char **words, *s;
1347  int i, wind;
1348
1349  words = history_tokenize_internal (line, ind, &wind);
1350  if (wind == -1)
1351    return ((char *)NULL);
1352  s = words[wind];
1353  for (i = 0; i < wind; i++)
1354    free (words[i]);
1355  for (i = wind + 1; words[i]; i++)
1356    free (words[i]);
1357  free (words);
1358  return s;
1359}
Note: See TracBrowser for help on using the repository browser.