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

📄 complete.c

📁 UNIX下SH的实现源码
💻 C
📖 第 1 页 / 共 4 页
字号:
{
  int printed_len = 0;
#if !defined (VISIBLE_STATS)
  char *s;

  for (s = to_print; *s; s++)
    {
      PUTX (*s);
    }
#else  
  char *s, c, *new_full_pathname;
  int extension_char, slen, tlen;

  for (s = to_print; *s; s++)
    {
      PUTX (*s);
    }

 if (rl_filename_completion_desired && rl_visible_stats)
    {
      /* If to_print != full_pathname, to_print is the basename of the
	 path passed.  In this case, we try to expand the directory
	 name before checking for the stat character. */
      if (to_print != full_pathname)
	{
	  /* Terminate the directory name. */
	  c = to_print[-1];
	  to_print[-1] = '\0';

	  /* If setting the last slash in full_pathname to a NUL results in
	     full_pathname being the empty string, we are trying to complete
	     files in the root directory.  If we pass a null string to the
	     bash directory completion hook, for example, it will expand it
	     to the current directory.  We just want the `/'. */
	  s = tilde_expand (full_pathname && *full_pathname ? full_pathname : "/");
	  if (rl_directory_completion_hook)
	    (*rl_directory_completion_hook) (&s);

	  slen = strlen (s);
	  tlen = strlen (to_print);
	  new_full_pathname = xmalloc (slen + tlen + 2);
	  strcpy (new_full_pathname, s);
	  new_full_pathname[slen] = '/';
	  strcpy (new_full_pathname + slen + 1, to_print);

	  extension_char = stat_char (new_full_pathname);

	  free (new_full_pathname);
	  to_print[-1] = c;
	}
      else
	{
	  s = tilde_expand (full_pathname);
	  extension_char = stat_char (s);
	}

      free (s);
      if (extension_char)
	{
	  putc (extension_char, rl_outstream);
	  printed_len++;
	}
    }
#endif /* VISIBLE_STATS */
  return printed_len;
}

static char *
rl_quote_filename (s, rtype, qcp)
     char *s;
     int rtype;
     char *qcp;
{
  char *r;

  r = xmalloc (strlen (s) + 2);
  *r = *rl_completer_quote_characters;
  strcpy (r + 1, s);
  if (qcp)
    *qcp = *rl_completer_quote_characters;
  return r;
}

/* Find the bounds of the current word for completion purposes, and leave
   rl_point set to the end of the word.  This function skips quoted
   substrings (characters between matched pairs of characters in
   rl_completer_quote_characters.  First we try to find an unclosed
   quoted substring on which to do matching.  If one is not found, we use
   the word break characters to find the boundaries of the current word.
   We call an application-specific function to decide whether or not a
   particular word break character is quoted; if that function returns a
   non-zero result, the character does not break a word.  This function
   returns the opening quote character if we found an unclosed quoted
   substring, '\0' otherwise.  FP, if non-null, is set to a value saying
   which (shell-like) quote characters we found (single quote, double
   quote, or backslash) anywhere in the string.  DP, if non-null, is set to
   the value of the delimiter character that caused a word break. */

static char
find_completion_word (fp, dp)
     int *fp, *dp;
{
  int scan, end, found_quote, delimiter, pass_next, isbrk;
  char quote_char;

  end = rl_point;
  found_quote = delimiter = 0;
  quote_char = '\0';

  if (rl_completer_quote_characters)
    {
      /* We have a list of characters which can be used in pairs to
	 quote substrings for the completer.  Try to find the start
	 of an unclosed quoted substring. */
      /* FOUND_QUOTE is set so we know what kind of quotes we found. */
      for (scan = pass_next = 0; scan < end; scan++)
	{
	  if (pass_next)
	    {
	      pass_next = 0;
	      continue;
	    }

	  if (rl_line_buffer[scan] == '\\')
	    {
	      pass_next = 1;
	      found_quote |= RL_QF_BACKSLASH;
	      continue;
	    }

	  if (quote_char != '\0')
	    {
	      /* Ignore everything until the matching close quote char. */
	      if (rl_line_buffer[scan] == quote_char)
		{
		  /* Found matching close.  Abandon this substring. */
		  quote_char = '\0';
		  rl_point = end;
		}
	    }
	  else if (strchr (rl_completer_quote_characters, rl_line_buffer[scan]))
	    {
	      /* Found start of a quoted substring. */
	      quote_char = rl_line_buffer[scan];
	      rl_point = scan + 1;
	      /* Shell-like quoting conventions. */
	      if (quote_char == '\'')
		found_quote |= RL_QF_SINGLE_QUOTE;
	      else if (quote_char == '"')
		found_quote |= RL_QF_DOUBLE_QUOTE;
	    }
	}
    }

  if (rl_point == end && quote_char == '\0')
    {
      /* We didn't find an unclosed quoted substring upon which to do
         completion, so use the word break characters to find the
         substring on which to complete. */
      while (--rl_point)
	{
	  scan = rl_line_buffer[rl_point];

	  if (strchr (rl_completer_word_break_characters, scan) == 0)
	    continue;

#if defined (HAVE_DOS_STYLE_FILE_SYSTEM)
	  if (scan == ':' && (rl_completion_entry_function == NULL
	      || rl_completion_entry_function == (Function *)filename_completion_function))
	    continue;
#endif

	  /* Call the application-specific function to tell us whether
	     this word break character is quoted and should be skipped. */
	  if (rl_char_is_quoted_p && found_quote &&
	      (*rl_char_is_quoted_p) (rl_line_buffer, rl_point))
	    continue;

	  /* Convoluted code, but it avoids an n^2 algorithm with calls
	     to char_is_quoted. */
	  break;
	}
    }

  /* If we are at an unquoted word break, then advance past it. */
  scan = rl_line_buffer[rl_point];

  /* If there is an application-specific function to say whether or not
     a character is quoted and we found a quote character, let that
     function decide whether or not a character is a word break, even
     if it is found in rl_completer_word_break_characters.  Don't bother
     if we're at the end of the line, though. */
  if (scan)
    {
      if (rl_char_is_quoted_p)
	isbrk = (found_quote == 0 ||
		(*rl_char_is_quoted_p) (rl_line_buffer, rl_point) == 0) &&
		strchr (rl_completer_word_break_characters, scan) != 0;
      else
	isbrk = strchr (rl_completer_word_break_characters, scan) != 0;

      if (isbrk)
	{
	  /* If the character that caused the word break was a quoting
	     character, then remember it as the delimiter. */
	  if (rl_basic_quote_characters &&
	      strchr (rl_basic_quote_characters, scan) &&
	      (end - rl_point) > 1)
	    delimiter = scan;

	  /* If the character isn't needed to determine something special
	     about what kind of completion to perform, then advance past it. */
	  if (rl_special_prefixes == 0 || strchr (rl_special_prefixes, scan) == 0)
	    rl_point++;
	}
    }

  if (fp)
    *fp = found_quote;
  if (dp)
    *dp = delimiter;

  return (quote_char);
}

static char **
gen_completion_matches (text, start, end, our_func, found_quote, quote_char)
     char *text;
     int start, end;
     Function *our_func;
     int found_quote, quote_char;
{
  char **matches, *temp;

  /* If the user wants to TRY to complete, but then wants to give
     up and use the default completion function, they set the
     variable rl_attempted_completion_function. */
  if (rl_attempted_completion_function)
    {
      matches = (*rl_attempted_completion_function) (text, start, end);

      if (matches || rl_attempted_completion_over)
	{
	  rl_attempted_completion_over = 0;
	  return (matches);
	}
    }

  /* Beware -- we're stripping the quotes here.  Do this only if we know
     we are doing filename completion and the application has defined a
     filename dequoting function. */
  temp = (char *)NULL;

  if (found_quote && our_func == (Function *)filename_completion_function &&
      rl_filename_dequoting_function)
    {
      /* delete single and double quotes */
      temp = (*rl_filename_dequoting_function) (text, quote_char);
      text = temp;	/* not freeing text is not a memory leak */
    }

  matches = completion_matches (text, (CPFunction *)our_func);
  FREE (temp);
  return matches;  
}

/* Filter out duplicates in MATCHES.  This frees up the strings in
   MATCHES. */
static char **
remove_duplicate_matches (matches)
     char **matches;
{
  char *lowest_common;
  int i, j, newlen;
  char dead_slot;
  char **temp_array;

  /* Sort the items. */
  for (i = 0; matches[i]; i++)
    ;

  /* Sort the array without matches[0], since we need it to
     stay in place no matter what. */
  if (i)
    qsort (matches+1, i-1, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare);

  /* Remember the lowest common denominator for it may be unique. */
  lowest_common = savestring (matches[0]);

  for (i = newlen = 0; matches[i + 1]; i++)
    {
      if (strcmp (matches[i], matches[i + 1]) == 0)
	{
	  free (matches[i]);
	  matches[i] = (char *)&dead_slot;
	}
      else
	newlen++;
    }

  /* We have marked all the dead slots with (char *)&dead_slot.
     Copy all the non-dead entries into a new array. */
  temp_array = (char **)xmalloc ((3 + newlen) * sizeof (char *));
  for (i = j = 1; matches[i]; i++)
    {
      if (matches[i] != (char *)&dead_slot)
	temp_array[j++] = matches[i];
    }
  temp_array[j] = (char *)NULL;

  if (matches[0] != (char *)&dead_slot)
    free (matches[0]);

  /* Place the lowest common denominator back in [0]. */
  temp_array[0] = lowest_common;

  /* If there is one string left, and it is identical to the
     lowest common denominator, then the LCD is the string to
     insert. */
  if (j == 2 && strcmp (temp_array[0], temp_array[1]) == 0)
    {
      free (temp_array[1]);
      temp_array[1] = (char *)NULL;
    }
  return (temp_array);
}

/* Find the common prefix of the list of matches, and put it into
   matches[0]. */
static int
compute_lcd_of_matches (match_list, matches, text)
     char **match_list;
     int matches;
     char *text;
{
  register int i, c1, c2, si;
  int low;		/* Count of max-matched characters. */

  /* If only one match, just use that.  Otherwise, compare each
     member of the list with the next, finding out where they
     stop matching. */
  if (matches == 1)
    {
      match_list[0] = match_list[1];
      match_list[1] = (char *)NULL;
      return 1;
    }

  for (i = 1, low = 100000; i < matches; i++)
    {
      if (_rl_completion_case_fold)
	{
	  for (si = 0;
	       (c1 = _rl_to_lower(match_list[i][si])) &&
	       (c2 = _rl_to_lower(match_list[i + 1][si]));
	       si++)
	    if (c1 != c2)
	      break;
	}
      else
	{
	  for (si = 0;
	       (c1 = match_list[i][si]) &&
	       (c2 = match_list[i + 1][si]);
	       si++)
	    if (c1 != c2)
	      break;
	}

      if (low > si)
	low = si;
    }

  /* If there were multiple matches, but none matched up to even the
     first character, and the user typed something, use that as the
     value of matches[0]. */
  if (low == 0 && text && *text)
    {
      match_list[0] = xmalloc (strlen (text) + 1);
      strcpy (match_list[0], text);
    }
  else
    {
      match_list[0] = xmalloc (low + 1);
      strncpy (match_list[0], match_list[1], low);
      match_list[0][low] = '\0';
    }

  return matches;
}

static int
postprocess_matches (matchesp, matching_filenames)
     char ***matchesp;
     int matching_filenames;
{
  char *t, **matches, **temp_matches;
  int nmatch, i;

  matches = *matchesp;

  /* It seems to me that in all the cases we handle we would like
     to ignore duplicate possiblilities.  Scan for the text to
     insert being identical to the other completions. */
  if (rl_ignore_completion_duplicates)
    {
      temp_matches = remove_duplicate_matches (matches);
      free (matches);
      matches = temp_matches;
    }

  /* If we are matching filenames, then here is our chance to
     do clever processing by re-examining the list.  Call the
     ignore function with the array as a parameter.  It can
     munge the array, deleting matches as it desires. */
  if (rl_ignore_some_completions_function && matching_filenames)
    {
      for (nmatch = 1; matches[nmatch]; nmatch++)
	;
      (void)(*rl_ignore_some_completions_function) (matches);
      if (matches == 0 || matches[0] == 0)
	{
	  FREE (matches);
	  *matchesp = (char **)0;
	  return 0;
        }
      else
	{
	  /* If we removed some matches, recompute the common prefix. */
	  for (i = 1; matches[i]; i++)
	    ;
	  if (i > 1 && i < nmatch)
	    {
	      t = matches[0];
	      compute_lcd_of_matches (matches, i - 1, t);
	      FREE (t);
	    }
	}
    }

  *matchesp = matches;
  return (1);
}

/* A convenience function for displaying a list of strings in
   columnar format on readline's output stream.  MATCHES is the list
   of strings, in argv format, LEN is the number of strings in MATCHES,
   and MAX is the length of the longest string in MATCHES. */
void
rl_display_match_list (matches, len, max)
     char **matches;

⌨️ 快捷键说明

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