OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [readline/] [histexpand.c] - Blame information for rev 1775

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.