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

📄 histexpand.c

📁 UNIX下SH的实现源码
💻 C
📖 第 1 页 / 共 3 页
字号:
	  new[j++] = subst_rhs[i];
	}
    }
  new[j] = '\0';
  free (subst_rhs);
  subst_rhs = new;
  subst_rhs_len = j;
}

/* Expand the bulk of a history specifier starting at STRING[START].
   Returns 0 if everything is OK, -1 if an error occurred, and 1
   if the `p' modifier was supplied and the caller should just print
   the returned string.  Returns the new index into string in
   *END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
static int
history_expand_internal (string, start, end_index_ptr, ret_string, current_line)
     char *string;
     int start, *end_index_ptr;
     char **ret_string;
     char *current_line;	/* for !# */
{
  int i, n, starting_index;
  int substitute_globally, want_quotes, print_only;
  char *event, *temp, *result, *tstr, *t, c, *word_spec;
  int result_len;

  result = xmalloc (result_len = 128);

  i = start;

  /* If it is followed by something that starts a word specifier,
     then !! is implied as the event specifier. */

  if (member (string[i + 1], ":$*%^"))
    {
      char fake_s[3];
      int fake_i = 0;
      i++;
      fake_s[0] = fake_s[1] = history_expansion_char;
      fake_s[2] = '\0';
      event = get_history_event (fake_s, &fake_i, 0);
    }
  else if (string[i + 1] == '#')
    {
      i += 2;
      event = current_line;
    }
  else
    {
      int quoted_search_delimiter = 0;

      /* If the character before this `!' is a double or single
	 quote, then this expansion takes place inside of the
	 quoted string.  If we have to search for some text ("!foo"),
	 allow the delimiter to end the search string. */
      if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
	quoted_search_delimiter = string[i - 1];
      event = get_history_event (string, &i, quoted_search_delimiter);
    }
	  
  if (event == 0)
    {
      *ret_string = hist_error (string, start, i, EVENT_NOT_FOUND);
      free (result);
      return (-1);
    }

  /* If a word specifier is found, then do what that requires. */
  starting_index = i;
  word_spec = get_history_word_specifier (string, event, &i);

  /* There is no such thing as a `malformed word specifier'.  However,
     it is possible for a specifier that has no match.  In that case,
     we complain. */
  if (word_spec == (char *)&error_pointer)
    {
      *ret_string = hist_error (string, starting_index, i, BAD_WORD_SPEC);
      free (result);
      return (-1);
    }

  /* If no word specifier, than the thing of interest was the event. */
  temp = word_spec ? savestring (word_spec) : savestring (event);
  FREE (word_spec);

  /* Perhaps there are other modifiers involved.  Do what they say. */
  want_quotes = substitute_globally = print_only = 0;
  starting_index = i;

  while (string[i] == ':')
    {
      c = string[i + 1];

      if (c == 'g')
	{
	  substitute_globally = 1;
	  i++;
	  c = string[i + 1];
	}

      switch (c)
	{
	default:
	  *ret_string = hist_error (string, i+1, i+2, BAD_MODIFIER);
	  free (result);
	  free (temp);
	  return -1;

	case 'q':
	  want_quotes = 'q';
	  break;

	case 'x':
	  want_quotes = 'x';
	  break;

	  /* :p means make this the last executed line.  So we
	     return an error state after adding this line to the
	     history. */
	case 'p':
	  print_only++;
	  break;

	  /* :t discards all but the last part of the pathname. */
	case 't':
	  tstr = strrchr (temp, '/');
	  if (tstr)
	    {
	      tstr++;
	      t = savestring (tstr);
	      free (temp);
	      temp = t;
	    }
	  break;

	  /* :h discards the last part of a pathname. */
	case 'h':
	  tstr = strrchr (temp, '/');
	  if (tstr)
	    *tstr = '\0';
	  break;

	  /* :r discards the suffix. */
	case 'r':
	  tstr = strrchr (temp, '.');
	  if (tstr)
	    *tstr = '\0';
	  break;

	  /* :e discards everything but the suffix. */
	case 'e':
	  tstr = strrchr (temp, '.');
	  if (tstr)
	    {
	      t = savestring (tstr);
	      free (temp);
	      temp = t;
	    }
	  break;

	/* :s/this/that substitutes `that' for the first
	   occurrence of `this'.  :gs/this/that substitutes `that'
	   for each occurrence of `this'.  :& repeats the last
	   substitution.  :g& repeats the last substitution
	   globally. */

	case '&':
	case 's':
	  {
	    char *new_event, *t;
	    int delimiter, failed, si, l_temp;

	    if (c == 's')
	      {
		if (i + 2 < (int)strlen (string))
		  delimiter = string[i + 2];
		else
		  break;	/* no search delimiter */

		i += 3;

		t = get_subst_pattern (string, &i, delimiter, 0, &subst_lhs_len);
		/* An empty substitution lhs with no previous substitution
		   uses the last search string as the lhs. */
		if (t)
		  {
		    FREE (subst_lhs);
		    subst_lhs = t;
		  }
		else if (!subst_lhs)
		  {
		    if (search_string && *search_string)
		      {
			subst_lhs = savestring (search_string);
			subst_lhs_len = strlen (subst_lhs);
		      }
		    else
		      {
			subst_lhs = (char *) NULL;
			subst_lhs_len = 0;
		      }
		  }

		FREE (subst_rhs);
		subst_rhs = get_subst_pattern (string, &i, delimiter, 1, &subst_rhs_len);

		/* If `&' appears in the rhs, it's supposed to be replaced
		   with the lhs. */
		if (member ('&', subst_rhs))
		  postproc_subst_rhs ();
	      }
	    else
	      i += 2;

	    /* If there is no lhs, the substitution can't succeed. */
	    if (subst_lhs_len == 0)
	      {
		*ret_string = hist_error (string, starting_index, i, NO_PREV_SUBST);
		free (result);
		free (temp);
		return -1;
	      }

	    l_temp = strlen (temp);
	    /* Ignore impossible cases. */
	    if (subst_lhs_len > l_temp)
	      {
		*ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
		free (result);
		free (temp);
		return (-1);
	      }

	    /* Find the first occurrence of THIS in TEMP. */
	    si = 0;
	    for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
	      if (STREQN (temp+si, subst_lhs, subst_lhs_len))
		{
		  int len = subst_rhs_len - subst_lhs_len + l_temp;
		  new_event = xmalloc (1 + len);
		  strncpy (new_event, temp, si);
		  strncpy (new_event + si, subst_rhs, subst_rhs_len);
		  strncpy (new_event + si + subst_rhs_len,
			   temp + si + subst_lhs_len,
			   l_temp - (si + subst_lhs_len));
		  new_event[len] = '\0';
		  free (temp);
		  temp = new_event;

		  failed = 0;

		  if (substitute_globally)
		    {
		      si += subst_rhs_len;
		      l_temp = strlen (temp);
		      substitute_globally++;
		      continue;
		    }
		  else
		    break;
		}

	    if (substitute_globally > 1)
	      {
		substitute_globally = 0;
		continue;	/* don't want to increment i */
	      }

	    if (failed == 0)
	      continue;		/* don't want to increment i */

	    *ret_string = hist_error (string, starting_index, i, SUBST_FAILED);
	    free (result);
	    free (temp);
	    return (-1);
	  }
	}
      i += 2;
    }
  /* Done with modfiers. */
  /* Believe it or not, we have to back the pointer up by one. */
  --i;

  if (want_quotes)
    {
      char *x;

      if (want_quotes == 'q')
	x = single_quote (temp);
      else if (want_quotes == 'x')
	x = quote_breaks (temp);
      else
	x = savestring (temp);

      free (temp);
      temp = x;
    }

  n = strlen (temp);
  if (n >= result_len)
    result = xrealloc (result, n + 2);
  strcpy (result, temp);
  free (temp);

  *end_index_ptr = i;
  *ret_string = result;
  return (print_only);
}

/* Expand the string STRING, placing the result into OUTPUT, a pointer
   to a string.  Returns:

  -1) If there was an error in expansion.
   0) If no expansions took place (or, if the only change in
      the text was the de-slashifying of the history expansion
      character)
   1) If expansions did take place
   2) If the `p' modifier was given and the caller should print the result

  If an error ocurred in expansion, then OUTPUT contains a descriptive
  error message. */

#define ADD_STRING(s) \
	do \
	  { \
	    int sl = strlen (s); \
	    j += sl; \
	    if (j >= result_len) \
	      { \
		while (j >= result_len) \
		  result_len += 128; \
		result = xrealloc (result, result_len); \
	      } \
	    strcpy (result + j - sl, s); \
	  } \
	while (0)

#define ADD_CHAR(c) \
	do \
	  { \
	    if (j >= result_len - 1) \
	      result = xrealloc (result, result_len += 64); \
	    result[j++] = c; \
	    result[j] = '\0'; \
	  } \
	while (0)

int
history_expand (hstring, output)
     char *hstring;
     char **output;
{
  register int j;
  int i, r, l, passc, cc, modified, eindex, only_printing;
  char *string;

  /* The output string, and its length. */
  int result_len;
  char *result;

  /* Used when adding the string. */
  char *temp;

  /* Setting the history expansion character to 0 inhibits all
     history expansion. */
  if (history_expansion_char == 0)
    {
      *output = savestring (hstring);
      return (0);
    }
    
  /* Prepare the buffer for printing error messages. */
  result = xmalloc (result_len = 256);
  result[0] = '\0';

  only_printing = modified = 0;
  l = strlen (hstring);

  /* Grovel the string.  Only backslash and single quotes can quote the
     history escape character.  We also handle arg specifiers. */

  /* Before we grovel forever, see if the history_expansion_char appears
     anywhere within the text. */

  /* The quick substitution character is a history expansion all right.  That
     is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
     that is the substitution that we do. */
  if (hstring[0] == history_subst_char)
    {
      string = xmalloc (l + 5);

      string[0] = string[1] = history_expansion_char;
      string[2] = ':';
      string[3] = 's';
      strcpy (string + 4, hstring);
      l += 4;
    }
  else
    {
      string = hstring;
      /* If not quick substitution, still maybe have to do expansion. */

      /* `!' followed by one of the characters in history_no_expand_chars
	 is NOT an expansion. */
      for (i = 0; string[i]; i++)
	{
	  cc = string[i + 1];
	  /* The history_comment_char, if set, appearing that the beginning
	     of a word signifies that the rest of the line should not have
	     history expansion performed on it.
	     Skip the rest of the line and break out of the loop. */
	  if (history_comment_char && string[i] == history_comment_char &&
	      (i == 0 || member (string[i - 1], HISTORY_WORD_DELIMITERS)))
	    {
	      while (string[i])
		i++;
	      break;
	    }
	  else if (string[i] == history_expansion_char)
	    {
	      if (!cc || member (cc, history_no_expand_chars))
		continue;
	      /* If the calling application has set
		 history_inhibit_expansion_function to a function that checks
		 for special cases that should not be history expanded,
		 call the function and skip the expansion if it returns a
		 non-zero value. */
	      else if (history_inhibit_expansion_function &&
			(*history_inhibit_expansion_function) (string, i))
		continue;
	      else
		break;
	    }
	  /* XXX - at some point, might want to extend this to handle
		   double quotes as well. */
	  else if (history_quotes_inhibit_expansion && string[i] == '\'')
	    {
	      /* If this is bash, single quotes inhibit history expansion. */
	      i++;
	      hist_string_extract_single_quoted (string, &i);
	    }
	  else if (history_quotes_inhibit_expansion && string[i] == '\\')
	    {
	      /* If this is bash, allow backslashes to quote single
		 quotes and the history expansion character. */
	      if (cc == '\'' || cc == history_expansion_char)
		i++;
	    }
	}
	  
      if (string[i] != history_expansion_char)
	{
	  free (result);
	  *output = savestring (string);
	  return (0);

⌨️ 快捷键说明

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