⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 histexpand.c

📁 UNIX下SH的实现源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* histexpand.c -- history expansion. */

/* Copyright (C) 1989, 1992 Free Software Foundation, Inc.

   This file contains the GNU History Library (the Library), a set of
   routines for managing the text of previously typed lines.

   The Library is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   The Library is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

#define READLINE_LIBRARY

#if defined (HAVE_CONFIG_H)
#  include <config.h>
#endif

#include <stdio.h>

#if defined (HAVE_STDLIB_H)
#  include <stdlib.h>
#else
#  include "ansi_stdlib.h"
#endif /* HAVE_STDLIB_H */

#if defined (HAVE_UNISTD_H)
#  ifndef _MINIX
#    include <sys/types.h>
#  endif
#  include <unistd.h>
#endif

#if defined (HAVE_STRING_H)
#  include <string.h>
#else
#  include <strings.h>
#endif /* !HAVE_STRING_H */

#include "history.h"
#include "histlib.h"

#include "rlshell.h"
#include "xmalloc.h"

#define HISTORY_WORD_DELIMITERS		" \t\n;&()|<>"
#define HISTORY_QUOTE_CHARACTERS	"\"'`"

static char error_pointer;

static char *subst_lhs;
static char *subst_rhs;
static int subst_lhs_len;
static int subst_rhs_len;

static char *get_history_word_specifier __P((char *, char *, int *));
static char *history_find_word __P((char *, int));

static char *quote_breaks __P((char *));

/* Variables exported by this file. */
/* The character that represents the start of a history expansion
   request.  This is usually `!'. */
char history_expansion_char = '!';

/* The character that invokes word substitution if found at the start of
   a line.  This is usually `^'. */
char history_subst_char = '^';

/* During tokenization, if this character is seen as the first character
   of a word, then it, and all subsequent characters upto a newline are
   ignored.  For a Bourne shell, this should be '#'.  Bash special cases
   the interactive comment character to not be a comment delimiter. */
char history_comment_char = '\0';

/* The list of characters which inhibit the expansion of text if found
   immediately following history_expansion_char. */
char *history_no_expand_chars = " \t\n\r=";

/* If set to a non-zero value, single quotes inhibit history expansion.
   The default is 0. */
int history_quotes_inhibit_expansion = 0;

/* If set, this points to a function that is called to verify that a
   particular history expansion should be performed. */
Function *history_inhibit_expansion_function;

/* **************************************************************** */
/*								    */
/*			History Expansion			    */
/*								    */
/* **************************************************************** */

/* Hairy history expansion on text, not tokens.  This is of general
   use, and thus belongs in this library. */

/* The last string searched for by a !?string? search. */
static char *search_string;

/* The last string matched by a !?string? search. */
static char *search_match;

/* Return the event specified at TEXT + OFFSET modifying OFFSET to
   point to after the event specifier.  Just a pointer to the history
   line is returned; NULL is returned in the event of a bad specifier.
   You pass STRING with *INDEX equal to the history_expansion_char that
   begins this specification.
   DELIMITING_QUOTE is a character that is allowed to end the string
   specification for what to search for in addition to the normal
   characters `:', ` ', `\t', `\n', and sometimes `?'.
   So you might call this function like:
   line = get_history_event ("!echo:p", &index, 0);  */
char *
get_history_event (string, caller_index, delimiting_quote)
     char *string;
     int *caller_index;
     int delimiting_quote;
{
  register int i;
  register char c;
  HIST_ENTRY *entry;
  int which, sign, local_index, substring_okay;
  Function *search_func;
  char *temp;

  /* The event can be specified in a number of ways.

     !!   the previous command
     !n   command line N
     !-n  current command-line minus N
     !str the most recent command starting with STR
     !?str[?]
	  the most recent command containing STR

     All values N are determined via HISTORY_BASE. */

  i = *caller_index;

  if (string[i] != history_expansion_char)
    return ((char *)NULL);

  /* Move on to the specification. */
  i++;

  sign = 1;
  substring_okay = 0;

#define RETURN_ENTRY(e, w) \
	return ((e = history_get (w)) ? e->line : (char *)NULL)

  /* Handle !! case. */
  if (string[i] == history_expansion_char)
    {
      i++;
      which = history_base + (history_length - 1);
      *caller_index = i;
      RETURN_ENTRY (entry, which);
    }

  /* Hack case of numeric line specification. */
  if (string[i] == '-')
    {
      sign = -1;
      i++;
    }

  if (_rl_digit_p (string[i]))
    {
      /* Get the extent of the digits and compute the value. */
      for (which = 0; _rl_digit_p (string[i]); i++)
	which = (which * 10) + _rl_digit_value (string[i]);

      *caller_index = i;

      if (sign < 0)
	which = (history_length + history_base) - which;

      RETURN_ENTRY (entry, which);
    }

  /* This must be something to search for.  If the spec begins with
     a '?', then the string may be anywhere on the line.  Otherwise,
     the string must be found at the start of a line. */
  if (string[i] == '?')
    {
      substring_okay++;
      i++;
    }

  /* Only a closing `?' or a newline delimit a substring search string. */
  for (local_index = i; c = string[i]; i++)
    if ((!substring_okay && (whitespace (c) || c == ':' ||
	(history_search_delimiter_chars && member (c, history_search_delimiter_chars)) ||
	string[i] == delimiting_quote)) ||
	string[i] == '\n' ||
	(substring_okay && string[i] == '?'))
      break;

  which = i - local_index;
  temp = xmalloc (1 + which);
  if (which)
    strncpy (temp, string + local_index, which);
  temp[which] = '\0';

  if (substring_okay && string[i] == '?')
    i++;

  *caller_index = i;

#define FAIL_SEARCH() \
  do { \
    history_offset = history_length; free (temp) ; return (char *)NULL; \
  } while (0)

  /* If there is no search string, try to use the previous search string,
     if one exists.  If not, fail immediately. */
  if (*temp == '\0' && substring_okay)
    {
      if (search_string)
        {
          free (temp);
          temp = savestring (search_string);
        }
      else
        FAIL_SEARCH ();
    }

  search_func = substring_okay ? history_search : history_search_prefix;
  while (1)
    {
      local_index = (*search_func) (temp, -1);

      if (local_index < 0)
	FAIL_SEARCH ();

      if (local_index == 0 || substring_okay)
	{
	  entry = current_history ();
	  history_offset = history_length;
	
	  /* If this was a substring search, then remember the
	     string that we matched for word substitution. */
	  if (substring_okay)
	    {
	      FREE (search_string);
	      search_string = temp;

	      FREE (search_match);
	      search_match = history_find_word (entry->line, local_index);
	    }
	  else
	    free (temp);

	  return (entry->line);
	}

      if (history_offset)
	history_offset--;
      else
	FAIL_SEARCH ();
    }
#undef FAIL_SEARCH
#undef RETURN_ENTRY
}

/* Function for extracting single-quoted strings.  Used for inhibiting
   history expansion within single quotes. */

/* Extract the contents of STRING as if it is enclosed in single quotes.
   SINDEX, when passed in, is the offset of the character immediately
   following the opening single quote; on exit, SINDEX is left pointing
   to the closing single quote. */
static void
hist_string_extract_single_quoted (string, sindex)
     char *string;
     int *sindex;
{
  register int i;

  for (i = *sindex; string[i] && string[i] != '\''; i++)
    ;

  *sindex = i;
}

static char *
quote_breaks (s)
     char *s;
{
  register char *p, *r;
  char *ret;
  int len = 3;

  for (p = s; p && *p; p++, len++)
    {
      if (*p == '\'')
	len += 3;
      else if (whitespace (*p) || *p == '\n')
	len += 2;
    }

  r = ret = xmalloc (len);
  *r++ = '\'';
  for (p = s; p && *p; )
    {
      if (*p == '\'')
	{
	  *r++ = '\'';
	  *r++ = '\\';
	  *r++ = '\'';
	  *r++ = '\'';
	  p++;
	}
      else if (whitespace (*p) || *p == '\n')
	{
	  *r++ = '\'';
	  *r++ = *p++;
	  *r++ = '\'';
	}
      else
	*r++ = *p++;
    }
  *r++ = '\'';
  *r = '\0';
  return ret;
}

static char *
hist_error(s, start, current, errtype)
      char *s;
      int start, current, errtype;
{
  char *temp, *emsg;
  int ll, elen;

  ll = current - start;

  switch (errtype)
    {
    case EVENT_NOT_FOUND:
      emsg = "event not found";
      elen = 15;
      break;
    case BAD_WORD_SPEC:
      emsg = "bad word specifier";
      elen = 18;
      break;
    case SUBST_FAILED:
      emsg = "substitution failed";
      elen = 19;
      break;
    case BAD_MODIFIER:
      emsg = "unrecognized history modifier";
      elen = 29;
      break;
    case NO_PREV_SUBST:
      emsg = "no previous substitution";
      elen = 24;
      break;
    default:
      emsg = "unknown expansion error";
      elen = 23;
      break;
    }

  temp = xmalloc (ll + elen + 3);
  strncpy (temp, s + start, ll);
  temp[ll] = ':';
  temp[ll + 1] = ' ';
  strcpy (temp + ll + 2, emsg);
  return (temp);
}

/* Get a history substitution string from STR starting at *IPTR
   and return it.  The length is returned in LENPTR.

   A backslash can quote the delimiter.  If the string is the
   empty string, the previous pattern is used.  If there is
   no previous pattern for the lhs, the last history search
   string is used.

   If IS_RHS is 1, we ignore empty strings and set the pattern
   to "" anyway.  subst_lhs is not changed if the lhs is empty;
   subst_rhs is allowed to be set to the empty string. */

static char *
get_subst_pattern (str, iptr, delimiter, is_rhs, lenptr)
     char *str;
     int *iptr, delimiter, is_rhs, *lenptr;
{
  register int si, i, j, k;
  char *s = (char *) NULL;

  i = *iptr;

  for (si = i; str[si] && str[si] != delimiter; si++)
    if (str[si] == '\\' && str[si + 1] == delimiter)
      si++;

  if (si > i || is_rhs)
    {
      s = xmalloc (si - i + 1);
      for (j = 0, k = i; k < si; j++, k++)
	{
	  /* Remove a backslash quoting the search string delimiter. */
	  if (str[k] == '\\' && str[k + 1] == delimiter)
	    k++;
	  s[j] = str[k];
	}
      s[j] = '\0';
      if (lenptr)
	*lenptr = j;
    }

  i = si;
  if (str[i])
    i++;
  *iptr = i;

  return s;
}

static void
postproc_subst_rhs ()
{
  char *new;
  int i, j, new_size;

  new = xmalloc (new_size = subst_rhs_len + subst_lhs_len);
  for (i = j = 0; i < subst_rhs_len; i++)
    {
      if (subst_rhs[i] == '&')
	{
	  if (j + subst_lhs_len >= new_size)
	    new = xrealloc (new, (new_size = new_size * 2 + subst_lhs_len));
	  strcpy (new + j, subst_lhs);
	  j += subst_lhs_len;
	}
      else
	{
	  /* a single backslash protects the `&' from lhs interpolation */
	  if (subst_rhs[i] == '\\' && subst_rhs[i + 1] == '&')
	    i++;
	  if (j >= new_size)
	    new = xrealloc (new, new_size *= 2);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -