~ubuntu-branches/debian/jessie/lftp/jessie

« back to all changes in this revision

Viewing changes to readline-4.3/histexpand.c

  • Committer: Bazaar Package Importer
  • Author(s): Noèl Köthe
  • Date: 2004-06-01 04:01:39 UTC
  • Revision ID: james.westby@ubuntu.com-20040601040139-negt39q17jhkv3i4
Tags: upstream-3.0.5
Import upstream version 3.0.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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_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
#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;&()|<>"
 
53
#define HISTORY_QUOTE_CHARACTERS        "\"'`"
 
54
 
 
55
typedef int _hist_search_func_t PARAMS((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 *));
 
67
static char *history_find_word PARAMS((char *, int));
 
68
 
 
69
static char *quote_breaks PARAMS((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;
 
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);
 
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)
 
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]))
 
181
    {
 
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]);
 
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_MAX > 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 == ':' ||
 
224
          (history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
 
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; \
 
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);
 
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);
 
277
              search_string = temp;
 
278
 
 
279
              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 == '\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 == '\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;
 
367
 
 
368
  ll = current - start;
 
369
 
 
370
  switch (errtype)
 
371
    {
 
372
    case EVENT_NOT_FOUND:
 
373
      emsg = "event not found";
 
374
      elen = 15;
 
375
      break;
 
376
    case BAD_WORD_SPEC:
 
377
      emsg = "bad word specifier";
 
378
      elen = 18;
 
379
      break;
 
380
    case SUBST_FAILED:
 
381
      emsg = "substitution failed";
 
382
      elen = 19;
 
383
      break;
 
384
    case BAD_MODIFIER:
 
385
      emsg = "unrecognized history modifier";
 
386
      elen = 29;
 
387
      break;
 
388
    case NO_PREV_SUBST:
 
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
  temp = (char *)xmalloc (ll + elen + 3);
 
399
  strncpy (temp, s + start, ll);
 
400
  temp[ll] = ':';
 
401
  temp[ll + 1] = ' ';
 
402
  strcpy (temp + ll + 2, emsg);
 
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;
 
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_MAX > 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
          strcpy (new + j, subst_lhs);
 
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], ":$*%^"))
 
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_MAX > 1 && rl_byte_oriented == 0)
 
560
        {
 
561
          int c, l;
 
562
          l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY);
 
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_FOUND);
 
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_SPEC);
 
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) : savestring (event);
 
599
  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_MODIFIER);
 
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);
 
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);
 
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_MAX > 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);
 
716
                    subst_lhs = t;
 
717
                  }
 
718
                else if (!subst_lhs)
 
719
                  {
 
720
                    if (search_string && *search_string)
 
721
                      {
 
722
                        subst_lhs = savestring (search_string);
 
723
                        subst_lhs_len = strlen (subst_lhs);
 
724
                      }
 
725
                    else
 
726
                      {
 
727
                        subst_lhs = (char *) NULL;
 
728
                        subst_lhs_len = 0;
 
729
                      }
 
730
                  }
 
731
 
 
732
                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))
 
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_SUBST);
 
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_FAILED);
 
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))
 
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_FAILED);
 
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);
 
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
  strcpy (result, temp);
 
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) \
 
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
            strcpy (result + j - sl, s); \
 
863
          } \
 
864
        while (0)
 
865
 
 
866
#define ADD_CHAR(c) \
 
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_MAX];
 
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);
 
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
      strcpy (string + 4, hstring);
 
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_MAX > 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)))
 
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))
 
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);
 
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);
 
1021
          continue;
 
1022
        }
 
1023
 
 
1024
#if defined (HANDLE_MULTIBYTE)
 
1025
      if (MB_CUR_MAX > 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_MAX; 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);
 
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]);
 
1057
          break;
 
1058
 
 
1059
        case '\\':
 
1060
          passc++;
 
1061
          ADD_CHAR (tchar);
 
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);
 
1080
                free (temp);
 
1081
              }
 
1082
            else
 
1083
              ADD_CHAR (string[i]);
 
1084
            break;
 
1085
          }
 
1086
 
 
1087
        case -2:                /* history_comment_char */
 
1088
          if (i == 0 || member (string[i - 1], history_word_delimiters))
 
1089
            {
 
1090
              temp = (char *)xmalloc (l - i + 1);
 
1091
              strcpy (temp, string + i);
 
1092
              ADD_STRING (temp);
 
1093
              free (temp);
 
1094
              i = l;
 
1095
            }
 
1096
          else
 
1097
            ADD_CHAR (string[i]);
 
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))
 
1107
            {
 
1108
              ADD_CHAR (string[i]);
 
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
                  strcpy (temp, result);
 
1124
                  ADD_STRING (temp);
 
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);
 
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;
 
1187
  result = (char *)NULL;
 
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) : savestring (""));
 
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 (""));
 
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]) && expecting_word_spec)
 
1228
    {
 
1229
      for (first = 0; _rl_digit_p (spec[i]); i++)
 
1230
        first = (first * 10) + _rl_digit_value (spec[i]);
 
1231
    }
 
1232
  else
 
1233
    return ((char *)NULL);      /* 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]))
 
1247
        {
 
1248
          for (last = 0; _rl_digit_p (spec[i]); i++)
 
1249
            last = (last * 10) + _rl_digit_value (spec[i]);
 
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)
 
1294
    return ((char *)NULL);
 
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);
 
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
          strcpy (result + offset, list[i]);
 
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; string[i]; )
 
1364
    {
 
1365
      delimiter = 0;
 
1366
 
 
1367
      /* Skip leading whitespace. */
 
1368
      for (; string[i] && whitespace (string[i]); 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"))
 
1376
        {
 
1377
          i++;
 
1378
          goto got_token;
 
1379
        }
 
1380
 
 
1381
      if (member (string[i], "<>;&|$"))
 
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))
 
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)))
 
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)))
 
1436
            break;
 
1437
 
 
1438
          if (!delimiter && member (string[i], HISTORY_QUOTE_CHARACTERS))
 
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;
 
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));
 
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);
 
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
}