📄 complete.c
字号:
strchr (rl_completer_word_break_characters, scan))#else if (strchr (rl_completer_word_break_characters, scan))#endif { /* If the character that caused the word break was a quoting character, then remember it as the delimiter. */ if (strchr ("\"'", 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 || strchr (rl_special_prefixes, scan) == 0) rl_point++; } } /* At this point, we know we have an open quote if quote_char != '\0'. */ start = rl_point; rl_point = end; text = rl_copy_text (start, end); /* 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; our_func = (Function *)NULL; goto after_usual_completion; } }#if defined (SHELL) /* Beware -- we're stripping the quotes here. Do this only if we know we are doing filename completion. */ if (found_quote && our_func == (Function *)filename_completion_function) { /* delete single and double quotes */ replacement = _delete_quotes (text); free (text); text = replacement; replacement = (char *)0; }#endif /* SHELL */ matches = completion_matches (text, our_func); after_usual_completion: free (text); if (!matches) ding (); else { register int i; int should_quote; /* 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) { char *lowest_common; int j, newlen = 0; char dead_slot; char **temp_array; /* Sort the items. */ /* It is safe to sort this array, because the lowest common denominator found in matches[0] will remain in place. */ for (i = 0; matches[i]; i++); qsort (matches, i, sizeof (char *), compare_strings); /* Remember the lowest common denominator for it may be unique. */ lowest_common = savestring (matches[0]); for (i = 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]); free (matches); matches = temp_array; /* Place the lowest common denominator back in [0]. */ matches[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 (matches[0], matches[1]) == 0) { free (matches[1]); matches[1] = (char *)NULL; } } switch (what_to_do) { case TAB: case '!': /* 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 && our_func == (Function *)filename_completion_function) (void)(*rl_ignore_some_completions_function)(matches); /* If we are doing completion on quoted substrings, and any matches contain any of the completer_word_break_characters, then auto- matically prepend the substring with a quote character (just pick the first one from the list of such) if it does not already begin with a quote string. FIXME: Need to remove any such automatically inserted quote character when it no longer is necessary, such as if we change the string we are completing on and the new set of matches don't require a quoted substring. */ replacement = matches[0]; should_quote = matches[0] && rl_completer_quote_characters && rl_filename_completion_desired && rl_filename_quoting_desired; if (should_quote)#if defined (SHELL) should_quote = should_quote && (!quote_char || quote_char == '"');#else should_quote = should_quote && !quote_char;#endif if (should_quote) { int do_replace; do_replace = NO_MATCH; /* If there is a single match, see if we need to quote it. This also checks whether the common prefix of several matches needs to be quoted. If the common prefix should not be checked, add !matches[1] to the if clause. */ should_quote = rl_strpbrk (matches[0], rl_completer_word_break_characters) != 0;#if defined (SHELL) should_quote = should_quote || rl_strpbrk (matches[0], "#$`") != 0;#endif if (should_quote) do_replace = matches[1] ? MULT_MATCH : SINGLE_MATCH; if (do_replace != NO_MATCH) {#if defined (SHELL) /* Quote the replacement, since we found an embedded word break character in a potential match. */ char *rtext, *mtext; int rlen; extern char *double_quote (); /* in builtins/common.c */ /* If DO_REPLACE == MULT_MATCH, it means that there is more than one match. In this case, we do not add the closing quote or attempt to perform tilde expansion. If DO_REPLACE == SINGLE_MATCH, we try to perform tilde expansion, because double quotes inhibit tilde expansion by the shell. */ mtext = matches[0]; if (mtext[0] == '~' && do_replace == SINGLE_MATCH) mtext = tilde_expand (matches[0]); rtext = double_quote (mtext); if (mtext != matches[0]) free (mtext); rlen = strlen (rtext); replacement = xmalloc (rlen + 1); /* If we're completing on a quoted string where the user has already supplied the opening quote, we don't want the quote in the replacement text, and we reset QUOTE_CHAR to 0 to avoid an extra closing quote. */ if (quote_char == '"') { strcpy (replacement, rtext + 1); rlen--; quote_char = 0; } else strcpy (replacement, rtext); if (do_replace == MULT_MATCH) replacement[rlen - 1] = '\0'; free (rtext);#else /* !SHELL */ /* Found an embedded word break character in a potential match, so we need to prepend a quote character if we are replacing the completion string. */ replacement = xmalloc (strlen (matches[0]) + 2); quote_char = *rl_completer_quote_characters; *replacement = quote_char; strcpy (replacement + 1, matches[0]);#endif /* SHELL */ } } if (replacement) { rl_begin_undo_group (); rl_delete_text (start, rl_point); rl_point = start; rl_insert_text (replacement); rl_end_undo_group (); if (replacement != matches[0]) free (replacement); } /* If there are more matches, ring the bell to indicate. If this was the only match, and we are hacking files, check the file to see if it was a directory. If so, add a '/' to the name. If not, and we are at the end of the line, then add a space. */ if (matches[1]) { if (what_to_do == '!') goto display_matches; /* XXX */ else if (rl_editing_mode != vi_mode) ding (); /* There are other matches remaining. */ } else { char temp_string[4]; int temp_string_index = 0; if (quote_char) temp_string[temp_string_index++] = quote_char; temp_string[temp_string_index++] = delimiter ? delimiter : ' '; temp_string[temp_string_index++] = '\0'; if (rl_filename_completion_desired) { struct stat finfo; char *filename = tilde_expand (matches[0]); if ((stat (filename, &finfo) == 0) && S_ISDIR (finfo.st_mode)) { if (rl_line_buffer[rl_point] != '/') rl_insert_text ("/"); } else { if (rl_point == rl_end) rl_insert_text (temp_string); } free (filename); } else { if (rl_point == rl_end) rl_insert_text (temp_string); } } break; case '*': { int i = 1; rl_begin_undo_group (); rl_delete_text (start, rl_point); rl_point = start; if (matches[1]) { while (matches[i]) { rl_insert_text (matches[i++]); rl_insert_text (" "); } } else { rl_insert_text (matches[0]); rl_insert_text (" "); } rl_end_undo_group (); } break; case '?': { int len, count, limit, max; int j, k, l; /* Handle simple case first. What if there is only one answer? */ if (!matches[1]) { char *temp; temp = printable_part (matches[0]); crlf (); print_filename (temp, matches[0]); crlf (); goto restart; } /* There is more than one answer. Find out how many there are, and find out what the maximum printed length of a single entry is. */ display_matches: for (max = 0, i = 1; matches[i]; i++) { char *temp; int name_length; temp = printable_part (matches[i]); name_length = strlen (temp); if (name_length > max) max = name_length; } len = i - 1; /* If there are many items, then ask the user if she really wants to see them all. */ if (len >= rl_completion_query_items) { crlf (); fprintf (rl_outstream, "There are %d possibilities. Do you really", len); crlf (); fprintf (rl_outstream, "wish to see them all? (y or n)"); fflush (rl_outstream); if (!get_y_or_n ()) { crlf (); goto restart; } } /* How many items of MAX length can we fit in the screen window? */ max += 2; limit = screenwidth / max; if (limit != 1 && (limit * max == screenwidth)) limit--; /* Avoid a possible floating exception. If max > screenwidth, limit will be 0 and a divide-by-zero fault will result. */ if (limit == 0) limit = 1; /* How many iterations of the printing loop? */ count = (len + (limit - 1)) / limit; /* Watch out for special case. If LEN is less than LIMIT, then just do the inner printing loop. */ if (len < limit) count = 1; /* Sort the items if they are not already sorted. */ if (!rl_ignore_completion_duplicates) qsort (matches, len, sizeof (char *), compare_strings); /* Print the sorted items, up-and-down alphabetically, like ls might. */ crlf (); for (i = 1; i < count + 1; i++) { for (j = 0, l = i; j < limit; j++) { if (l > len || !matches[l]) break; else { char *temp; int printed_length; temp = printable_part (matches[l]); printed_length = strlen (temp); printed_length += print_filename (temp, matches[l]); if (j + 1 < limit) { for (k = 0; k < max - printed_length; k++) putc (' ', rl_outstream); } } l += count; } crlf (); } restart: rl_on_new_line (); } break; default: fprintf (stderr, "\r\nreadline: bad value for what_to_do in rl_complete\n"); abort (); } for (i = 0; matches[i]; i++) free (matches[i]); free (matches); } /* Check to see if the line has changed through all of this manipulation. */ if (saved_line_buffer) { if (strcmp (rl_line_buffer, saved_line_buffer) != 0) completion_changed_buffer = 1; else completion_changed_buffer = 0; free (saved_line_buffer); } return 0;}#if defined (VISIBLE_STATS)/* Return the character which best describes FILENAME. `@' for symbolic links `/' for directories `*' for executables `=' for sockets */static intstat_char (filename) char *filename;{ struct stat finfo; int character, r;#if defined (S_ISLNK) r = lstat (filename, &finfo);#else r = stat (filename, &finfo);#endif if (r == -1) return (0); character = 0; if (S_ISDIR (finfo.st_mode)) character = '/';#if defined (S_ISLNK) else if (S_ISLNK (finfo.st_mode)) character = '@';#endif /* S_ISLNK */#if defined (S_ISSOCK) else if (S_ISSOCK (finfo.st_mode)) character = '=';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -