📄 history.c
字号:
case SUBST_FAILED: emsg = "substitution failed"; elen = 19; break; case BAD_MODIFIER: emsg = "unrecognized history modifier"; elen = 29; 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 voidpostproc_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); 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 inthistory_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) { *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. */ if (!word_spec) temp = savestring (event); else { temp = savestring (word_spec); 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;#if defined (SHELL) case 'q': want_quotes = 'q'; break; case 'x': want_quotes = 'x'; break;#endif /* SHELL */ /* :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) { if (subst_lhs) 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; } } /* If there is no lhs, the substitution can't succeed. */ if (subst_lhs_len == 0) { *ret_string = hist_error (string, starting_index, i, SUBST_FAILED); free (result); free (temp); return -1; } if (subst_rhs) 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; 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 defined (SHELL) 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; }#endif /* SHELL */ 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)inthistory_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 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]; if (string[i] == history_expansion_char) { if (!cc || member (cc, history_no_expand_chars)) continue;#if defined (SHELL) /* The shell uses ! as a pattern negation character in globbing [...] expressions, so let those pass without expansion. */ else if (i > 0 && (string[i - 1] == '[') && member (']', string + i + 1)) continue;#endif /* SHELL */ else break; }#if defined (SHELL) else if (string[i] == '\'') { /* If this is bash, single quotes inhibit history expansion. */ i++; rl_string_extract_single_quoted (string, &i); } else if (string[i] == '\\') { /* If this is bash, allow backslashes to quote single quotes and the history expansion character. */ if (cc == '\'' || cc == history_expansion_char) i++; }#endif /* SHELL */ } if (string[i] != history_expansion_char) { free (result); *output = savestring (string); return (0); } } /* Extract and perform the substitution. */ for (passc = i = j = 0; i < l; i++)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -