📄 histexpand.c
字号:
if (tchar == history_expansion_char) tchar = -3; else if (tchar == history_comment_char) tchar = -2; switch (tchar) { default: ADD_CHAR (string[i]); break; case '\\': passc++; ADD_CHAR (tchar); break; case '"': dquote = 1 - dquote; ADD_CHAR (tchar); break; case '\'': { /* If history_quotes_inhibit_expansion is set, single quotes inhibit history expansion. */ if (dquote == 0 && history_quotes_inhibit_expansion) { int quote, slen; flag = (i > 0 && string[i - 1] == '$'); quote = i++; hist_string_extract_single_quoted (string, &i, flag); slen = i - quote + 2; temp = (char *)xmalloc (slen); strncpy (temp, string + quote, slen); temp[slen - 1] = '\0'; ADD_STRING (temp); xfree (temp); } else ADD_CHAR (string[i]); break; } case -2: /* history_comment_char */ if (i == 0 || member (string[i - 1], history_word_delimiters)) { temp = (char *)xmalloc (l - i + 1); strcpy (temp, string + i); ADD_STRING (temp); xfree (temp); i = l; } else ADD_CHAR (string[i]); break; case -3: /* history_expansion_char */ cc = string[i + 1]; /* If the history_expansion_char is followed by one of the characters in history_no_expand_chars, then it is not a candidate for expansion of any kind. */ if (cc == 0 || member (cc, history_no_expand_chars) || (history_inhibit_expansion_function && (*history_inhibit_expansion_function) (string, i))) { ADD_CHAR (string[i]); break; }#if defined (NO_BANG_HASH_MODIFIERS) /* There is something that is listed as a `word specifier' in csh documentation which means `the expanded text to this point'. That is not a word specifier, it is an event specifier. If we don't want to allow modifiers with `!#', just stick the current output line in again. */ if (cc == '#') { if (result) { temp = (char *)xmalloc (1 + strlen (result)); strcpy (temp, result); ADD_STRING (temp); xfree (temp); } i++; break; }#endif r = history_expand_internal (string, i, &eindex, &temp, result); if (r < 0) { *output = temp; xfree (result); if (string != hstring) xfree (string); return -1; } else { if (temp) { modified++; if (*temp) ADD_STRING (temp); xfree (temp); } only_printing = r == 1; i = eindex; } break; } } *output = result; if (string != hstring) xfree (string); if (only_printing) {#if 0 add_history (result);#endif return (2); } return (modified != 0);}/* Return a consed string which is the word specified in SPEC, and found in FROM. NULL is returned if there is no spec. The address of ERROR_POINTER is returned if the word specified cannot be found. CALLER_INDEX is the offset in SPEC to start looking; it is updated to point to just after the last character parsed. */static char *get_history_word_specifier (spec, from, caller_index) char *spec, *from; int *caller_index;{ register int i = *caller_index; int first, last; int expecting_word_spec = 0; char *result; /* The range of words to return doesn't exist yet. */ first = last = 0; result = (char *)NULL; /* If we found a colon, then this *must* be a word specification. If it isn't, then it is an error. */ if (spec[i] == ':') { i++; expecting_word_spec++; } /* Handle special cases first. */ /* `%' is the word last searched for. */ if (spec[i] == '%') { *caller_index = i + 1; return (search_match ? savestring (search_match) : savestring ("")); } /* `*' matches all of the arguments, but not the command. */ if (spec[i] == '*') { *caller_index = i + 1; result = history_arg_extract (1, '$', from); return (result ? result : savestring ("")); } /* `$' is last arg. */ if (spec[i] == '$') { *caller_index = i + 1; return (history_arg_extract ('$', '$', from)); } /* Try to get FIRST and LAST figured out. */ if (spec[i] == '-') first = 0; else if (spec[i] == '^') { first = 1; i++; } else if (_rl_digit_p (spec[i]) && expecting_word_spec) { for (first = 0; _rl_digit_p (spec[i]); i++) first = (first * 10) + _rl_digit_value (spec[i]); } else return ((char *)NULL); /* no valid `first' for word specifier */ if (spec[i] == '^' || spec[i] == '*') { last = (spec[i] == '^') ? 1 : '$'; /* x* abbreviates x-$ */ i++; } else if (spec[i] != '-') last = first; else { i++; if (_rl_digit_p (spec[i])) { for (last = 0; _rl_digit_p (spec[i]); i++) last = (last * 10) + _rl_digit_value (spec[i]); } else if (spec[i] == '$') { i++; last = '$'; }#if 0 else if (!spec[i] || spec[i] == ':') /* check against `:' because there could be a modifier separator */#else else /* csh seems to allow anything to terminate the word spec here, leaving it as an abbreviation. */#endif last = -1; /* x- abbreviates x-$ omitting word `$' */ } *caller_index = i; if (last >= first || last == '$' || last < 0) result = history_arg_extract (first, last, from); return (result ? result : (char *)&error_pointer);}/* Extract the args specified, starting at FIRST, and ending at LAST. The args are taken from STRING. If either FIRST or LAST is < 0, then make that arg count from the right (subtract from the number of tokens, so that FIRST = -1 means the next to last token on the line). If LAST is `$' the last arg from STRING is used. */char *history_arg_extract (first, last, string) int first, last; const char *string;{ register int i, len; char *result; int size, offset; char **list; /* XXX - think about making history_tokenize return a struct array, each struct in array being a string and a length to avoid the calls to strlen below. */ if ((list = history_tokenize (string)) == NULL) return ((char *)NULL); for (len = 0; list[len]; len++) ; if (last < 0) last = len + last - 1; if (first < 0) first = len + first - 1; if (last == '$') last = len - 1; if (first == '$') first = len - 1; last++; if (first >= len || last > len || first < 0 || last < 0 || first > last) result = ((char *)NULL); else { for (size = 0, i = first; i < last; i++) size += strlen (list[i]) + 1; result = (char *)xmalloc (size + 1); result[0] = '\0'; for (i = first, offset = 0; i < last; i++) { strcpy (result + offset, list[i]); offset += strlen (list[i]); if (i + 1 < last) { result[offset++] = ' '; result[offset] = 0; } } } for (i = 0; i < len; i++) xfree (list[i]); xfree (list); return (result);}static inthistory_tokenize_word (string, ind) const char *string; int ind;{ register int i; int delimiter, nestdelim, delimopen; i = ind; delimiter = nestdelim = 0; if (member (string[i], "()\n")) { i++; return i; } if (member (string[i], "<>;&|$")) { int peek = string[i + 1]; if (peek == string[i] && peek != '$') { if (peek == '<' && string[i + 2] == '-') i++; else if (peek == '<' && string[i + 2] == '<') i++; i += 2; return i; } else if ((peek == '&' && (string[i] == '>' || string[i] == '<')) || (peek == '>' && string[i] == '&')) { i += 2; return i; } /* XXX - separated out for later -- bash-4.2 */ else if ((peek == '(' && (string[i] == '>' || string[i] == '<')) || /* ) */ (peek == '(' && string[i] == '$')) /*)*/ { i += 2; delimopen = '('; delimiter = ')'; nestdelim = 1; goto get_word; }#if 0 else if (peek == '\'' && string[i] == '$') { i += 2; /* XXX */ return i; }#endif if (string[i] != '$') { i++; return i; } } /* same code also used for $(...)/<(...)/>(...) above */ if (member (string[i], "!@?+*")) { int peek = string[i + 1]; if (peek == '(') /*)*/ { /* Shell extended globbing patterns */ i += 2; delimopen = '('; delimiter = ')'; /* XXX - not perfect */ nestdelim = 1; } }get_word: /* Get word from string + i; */ if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS)) delimiter = string[i++]; for (; string[i]; i++) { if (string[i] == '\\' && string[i + 1] == '\n') { i++; continue; } if (string[i] == '\\' && delimiter != '\'' && (delimiter != '"' || member (string[i], slashify_in_quotes))) { i++; continue; } /* delimiter must be set and set to something other than a quote if nestdelim is set, so these tests are safe. */ if (nestdelim && string[i] == delimopen) { nestdelim++; continue; } if (nestdelim && string[i] == delimiter) { nestdelim--; if (nestdelim == 0) delimiter = 0; continue; } if (delimiter && string[i] == delimiter) { delimiter = 0; continue; } if (delimiter == 0 && (member (string[i], history_word_delimiters))) break; if (delimiter == 0 && member (string[i], HISTORY_QUOTE_CHARACTERS)) delimiter = string[i]; } return i;}static char *history_substring (string, start, end) const char *string; int start, end;{ register int len; register char *result; len = end - start; result = (char *)xmalloc (len + 1); strncpy (result, string + start, len); result[len] = '\0'; return result;}/* Parse STRING into tokens and return an array of strings. If WIND is not -1 and INDP is not null, we also want the word surrounding index WIND. The position in the returned array of strings is returned in *INDP. */static char **history_tokenize_internal (string, wind, indp) const char *string; int wind, *indp;{ char **result; register int i, start, result_index, size; /* If we're searching for a string that's not part of a word (e.g., " "), make sure we set *INDP to a reasonable value. */ if (indp && wind != -1) *indp = -1; /* Get a token, and stuff it into RESULT. The tokens are split exactly where the shell would split them. */ for (i = result_index = size = 0, result = (char **)NULL; string[i]; ) { /* Skip leading whitespace. */ for (; string[i] && whitespace (string[i]); i++) ; if (string[i] == 0 || string[i] == history_comment_char) return (result); start = i; i = history_tokenize_word (string, start); /* If we have a non-whitespace delimiter character (which would not be skipped by the loop above), use it and any adjacent delimiters to make a separate field. Any adjacent white space will be skipped the next time through the loop. */ if (i == start && history_word_delimiters) { i++; while (string[i] && member (string[i], history_word_delimiters)) i++; } /* If we are looking for the word in which the character at a particular index falls, remember it. */ if (indp && wind != -1 && wind >= start && wind < i) *indp = result_index; if (result_index + 2 >= size) result = (char **)xrealloc (result, ((size += 10) * sizeof (char *))); result[result_index++] = history_substring (string, start, i); result[result_index] = (char *)NULL; } return (result);}/* Return an array of tokens, much as the shell might. The tokens are parsed out of STRING. */char **history_tokenize (string) const char *string;{ return (history_tokenize_internal (string, -1, (int *)NULL));}/* Free members of WORDS from START to an empty string */static voidfreewords (words, start) char **words; int start;{ register int i; for (i = start; words[i]; i++) xfree (words[i]);}/* Find and return the word which contains the character at index IND in the history line LINE. Used to save the word matched by the last history !?string? search. */static char *history_find_word (line, ind) char *line; int ind;{ char **words, *s; int i, wind; words = history_tokenize_internal (line, ind, &wind); if (wind == -1 || words == 0) { if (words) freewords (words, 0); FREE (words); return ((char *)NULL); } s = words[wind]; for (i = 0; i < wind; i++) xfree (words[i]); freewords (words, wind + 1); xfree (words); return s;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -