TheGNUGeneralPublicLicenseisoftenshippedwithGNUsoftware,and isgenerallykeptinafilecalledCOPYINGorLICENSE.Ifyoudonot haveacopyofthelicense,writetotheFreeSoftwareFoundation,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA. */
#define READLINE_LIBRARY
#ifdefined (HAVE_CONFIG_H) # include "config_readline.h" #endif
#include <stdio.h>
#ifdefined (HAVE_STDLIB_H) # include <stdlib.h> #else # include "ansi_stdlib.h" #endif/* HAVE_STDLIB_H */
#ifdefined (HAVE_UNISTD_H) # ifndef _MINIX # include <sys/types.h> # endif # include <unistd.h> #endif
/* Variables exported by this file. */ /* The character that represents the start of a history expansion
request. This is usually `!'. */ char history_expansion_char = '!';
/* The character that invokes word substitution if found at the start of
a line. This is usually `^'. */ char history_subst_char = '^';
/* During tokenization, if this character is seen as the first character ofaword,thenit,andallsubsequentcharactersuptoanewlineare ignored.ForaBourneshell,thisshouldbe'#'.Bashspecialcases
the interactive comment character to not be a comment delimiter. */ char history_comment_char = '\0';
/* The list of characters which inhibit the expansion of text if found
immediately following history_expansion_char. */ constchar *history_no_expand_chars = " \t\n\r=";
/* If set to a non-zero value, single quotes inhibit history expansion.
The default is 0. */ int history_quotes_inhibit_expansion = 0;
/* Used to split words by history_tokenize_internal. */ constchar *history_word_delimiters = HISTORY_WORD_DELIMITERS;
/* If set, this points to a function that is called to verify that a
particular history expansion should be performed. */
rl_linebuf_func_t *history_inhibit_expansion_function;
/* Hack case of numeric line specification. */ if (string[i] == '-')
{
sign = -1;
i++;
}
if (_rl_digit_p (string[i]))
{ /* Get the extent of the digits and compute the value. */ for (which = 0; _rl_digit_p (string[i]); i++)
which = (which * 10) + _rl_digit_value (string[i]);
*caller_index = i;
if (sign < 0)
which = (history_length + history_base) - which;
RETURN_ENTRY (entry, which);
}
/* This must be something to search for. If the spec begins with a'?',thenthestringmaybeanywhereontheline.Otherwise,
the string must be found at the start of a line. */ if (string[i] == '?')
{
substring_okay++;
i++;
}
/* Only a closing `?' or a newline delimit a substring search string. */ for (local_index = i; (c = string[i]); i++)
{ #ifdefined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{ int v;
mbstate_t ps;
memset (&ps, 0, sizeof (mbstate_t)); /* These produce warnings because we're passing a const string to a
function that takes a non-const string. */
_rl_adjust_point ((char *)string, i, &ps); if ((v = _rl_get_char_len ((char *)string + i, &ps)) > 1)
{
i += v - 1; continue;
}
}
which = i - local_index;
temp = (char *)xmalloc (1 + which); if (which)
strncpy (temp, string + local_index, which);
temp[which] = '\0';
if (substring_okay && string[i] == '?')
i++;
*caller_index = i;
#define FAIL_SEARCH() \ do { \
history_offset = history_length; free (temp) ; return (char *)NULL; \
} while (0)
/* If there is no search string, try to use the previous search string,
if one exists. If not, fail immediately. */ if (*temp == '\0' && substring_okay)
{ if (search_string)
{
free (temp);
temp = savestring (search_string);
} else
FAIL_SEARCH ();
}
/* If this was a substring search, then remember the
string that we matched for word substitution. */ if (substring_okay)
{
FREE (search_string);
search_string = temp;
/* Function for extracting single-quoted strings. Used for inhibiting
history expansion within single quotes. */
/* Extract the contents of STRING as if it is enclosed in single quotes. SINDEX,whenpassedin,istheoffsetofthecharacterimmediately followingtheopeningsinglequote;onexit,SINDEXisleftpointing
to the closing single quote. */ staticvoid
hist_string_extract_single_quoted (string, sindex) char *string; int *sindex;
{ registerint i;
for (i = *sindex; string[i] && string[i] != '\''; i++)
;
*sindex = i;
}
staticchar *
quote_breaks (s) char *s;
{ registerchar *p, *r; char *ret; int len = 3;
for (p = s; p && *p; p++, len++)
{ if (*p == '\'')
len += 3; elseif (whitespace (*p) || *p == '\n')
len += 2;
}
IfIS_RHSis1,weignoreemptystringsandsetthepattern to""anyway.subst_lhsisnotchangedifthelhsisempty;
subst_rhs is allowed to be set to the empty string. */
for (si = i; str[si] && str[si] != delimiter; si++) #ifdefined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{ int v; if ((v = _rl_get_char_len (str + si, &ps)) > 1)
si += v - 1; elseif (str[si] == '\\' && str[si + 1] == delimiter)
si++;
} else #endif/* HANDLE_MULTIBYTE */ if (str[si] == '\\' && str[si + 1] == delimiter)
si++;
if (si > i || is_rhs)
{
s = (char *)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;
}
staticvoid
postproc_subst_rhs ()
{ char *new; int i, j, new_size;
new = (char *)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 = (char *)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 = (char *)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]. Returns0ifeverythingisOK,-1ifanerroroccurred,and1 ifthe`p'modifierwassuppliedandthecallershouldjustprint thereturnedstring.Returnsthenewindexintostringin
*END_INDEX_PTR, and the expanded specifier in *RET_STRING. */ staticint
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, subst_bywords, want_quotes, print_only; char *event, *temp, *result, *tstr, *t, c, *word_spec; int result_len; #ifdefined (HANDLE_MULTIBYTE)
mbstate_t ps;
memset (&ps, 0, sizeof (mbstate_t)); #endif
result = (char *)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 the character before this `!' is a double or single quote,thenthisexpansiontakesplaceinsideofthe quotedstring.Ifwehavetosearchforsometext("!foo"),
allow the delimiter to end the search string. */ #ifdefined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{ int ch, l;
l = _rl_find_prev_mbchar (string, i, MB_FIND_ANY);
ch = string[l]; /* XXX - original patch had i - 1 ??? If i == 0 it would fail. */ if (i && (ch == '\'' || ch == '"'))
quoted_search_delimiter = ch;
} else #endif/* HANDLE_MULTIBYTE */ if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
quoted_search_delimiter = string[i - 1];
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, itispossibleforaspecifierthathasnomatch.Inthatcase,
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 = subst_bywords = print_only = 0;
starting_index = i;
while (string[i] == ':')
{
c = string[i + 1];
if (c == 'g' || c == 'a')
{
substitute_globally = 1;
i++;
c = string[i + 1];
} elseif (c == 'G')
{
subst_bywords = 1;
i++;
c = string[i + 1];
}
/* :p means make this the last executed line. So we returnanerrorstateafteraddingthislinetothe
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 occurrenceof`this'.:gs/this/thatsubstitutes`that' foreachoccurrenceof`this'.:&repeatsthelast substitution.:g&repeatsthelastsubstitution
globally. */
/* 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;
}
Inthefirstcase,wesubstituteforthefirstoccurrenceonly. Inthesecondcase,wesubstituteforeveryoccurrence. Inthethirdcase,wetokenizeintowordsandsubstitutethe
first occurrence of each word. */
si = we = 0; for (failed = 1; (si + subst_lhs_len) <= l_temp; si++)
{ /* First skip whitespace and find word boundaries if we'repasttheendofthewordboundarywefound
the last time. */ if (subst_bywords && si > we)
{ for (; temp[si] && whitespace (temp[si]); si++)
;
we = history_tokenize_word (temp, si);
}
if (substitute_globally)
{ /* Reported to fix a bug that causes it to skip every othermatchwhenmatchingasinglecharacter.Was
si += subst_rhs_len previously. */
si += subst_rhs_len - 1;
l_temp = strlen (temp);
substitute_globally++; continue;
} elseif (subst_bywords)
{
si = we;
l_temp = strlen (temp); 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 = sh_single_quote (temp); elseif (want_quotes == 'x')
x = quote_breaks (temp); else
x = savestring (temp);
free (temp);
temp = x;
}
n = strlen (temp); if (n >= result_len)
result = (char *)xrealloc (result, n + 2);
strcpy (result, temp);
free (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 = (char *)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 istosay,"^this^that^"isequivalentto"!!:s^this^that^",andinfact,
that is the substitution that we do. */ if (hstring[0] == history_subst_char)
{
string = (char *)xmalloc (l + 5);
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 = dquote = 0; string[i]; i++)
{ #ifdefined (HANDLE_MULTIBYTE) if (MB_CUR_MAX > 1 && rl_byte_oriented == 0)
{ int v;
v = _rl_get_char_len (string + i, &ps); if (v > 1)
{
i += v - 1; continue;
}
} #endif/* HANDLE_MULTIBYTE */
cc = string[i + 1]; /* The history_comment_char, if set, appearing at the beginning ofawordsignifiesthattherestofthelineshouldnothave historyexpansionperformedonit.
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;
} elseif (string[i] == history_expansion_char)
{ if (!cc || member (cc, history_no_expand_chars)) continue; /* If the calling application has set history_inhibit_expansion_functiontoafunctionthatchecks forspecialcasesthatshouldnotbehistoryexpanded, callthefunctionandskiptheexpansionifitreturnsa
non-zero value. */ elseif (history_inhibit_expansion_function &&
(*history_inhibit_expansion_function) (string, i)) continue; else break;
} /* Shell-like quoting: allow backslashes to quote double quotes
inside a double-quoted string. */ elseif (dquote && string[i] == '\\' && cc == '"')
i++; /* More shell-like quoting: if we're paying attention to single quotesandlettingthemquotethehistoryexpansioncharacter, thenweneedtopayattentiontodoublequotes,becausesingle
quotes are not special inside double-quoted strings. */ elseif (history_quotes_inhibit_expansion && string[i] == '"')
{
dquote = 1 - dquote;
} elseif (dquote == 0 && history_quotes_inhibit_expansion && string[i] == '\'')
{ /* If this is bash, single quotes inhibit history expansion. */
i++;
hist_string_extract_single_quoted (string, &i);
} elseif (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++;
}
case'\'':
{ /* If history_quotes_inhibit_expansion is set, single quotes
inhibit history expansion. */ if (dquote == 0 && history_quotes_inhibit_expansion)
{ int quote, slen;
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);
free (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 charactersinhistory_no_expand_chars,thenitisnota
candidate for expansion of any kind. */ if (member (cc, history_no_expand_chars))
{
ADD_CHAR (string[i]); break;
}
#ifdefined (NO_BANG_HASH_MODIFIERS) /* There is something that is listed as a `word specifier' in csh documentationwhichmeans`theexpandedtexttothispoint'. Thatisnotawordspecifier,itisaneventspecifier.Ifwe don'twanttoallowmodifierswith`!#',juststickthecurrent
output line in again. */ if (cc == '#')
{ if (result)
{
temp = (char *)xmalloc (1 + strlen (result));
strcpy (temp, result);
ADD_STRING (temp);
free (temp);
}
i++; break;
} #endif
r = history_expand_internal (string, i, &eindex, &temp, result); if (r < 0)
{
*output = temp;
free (result); if (string != hstring)
free (string); return -1;
} else
{ if (temp)
{
modified++; if (*temp)
ADD_STRING (temp);
free (temp);
}
only_printing = r == 1;
i = eindex;
} break;
}
}
*output = result; if (string != hstring)
free (string);
if (only_printing)
{ #if0
add_history (result); #endif return (2);
}
return (modified != 0);
}
/* Return a consed string which is the word specified in SPEC, and found inFROM.NULLisreturnedifthereisnospec.Theaddressof ERROR_POINTERisreturnedifthewordspecifiedcannotbefound. CALLER_INDEXistheoffsetinSPECtostartlooking;itisupdated
to point to just after the last character parsed. */ staticchar *
get_history_word_specifier (spec, from, caller_index) char *spec, *from; int *caller_index;
{ registerint 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; elseif (spec[i] == '^')
{
first = 1;
i++;
} elseif (_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 (_rl_digit_p (spec[i]))
{ for (last = 0; _rl_digit_p (spec[i]); i++)
last = (last * 10) + _rl_digit_value (spec[i]);
} elseif (spec[i] == '$')
{
i++;
last = '$';
} #if0 elseif (!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. TheargsaretakenfromSTRING.IfeitherFIRSTorLASTis<0, thenmakethatargcountfromtheright(subtractfromthenumberof tokens,sothatFIRST=-1meansthenexttolasttokenontheline).
If LAST is `$' the last arg from STRING is used. */ char *
history_arg_extract (first, last, string) int first, last; constchar *string;
{ registerint i, len; char *result; int size, offset; char **list;
/* XXX - think about making history_tokenize return a struct array, eachstructinarraybeingastringandalengthtoavoidthe
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++)
free (list[i]);
free (list);
return (result);
}
staticint
history_tokenize_word (string, ind) constchar *string; int ind;
{ registerint i; int delimiter;
i = ind;
delimiter = 0;
if (member (string[i], "()\n"))
{
i++; return i;
}
if (member (string[i], "<>;&|$"))
{ int peek = string[i + 1];
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-1andINDPisnotnull,wealsowantthewordsurroundingindex WIND.Thepositioninthereturnedarrayofstringsisreturnedin
*INDP. */ staticchar **
history_tokenize_internal (string, wind, indp) constchar *string; int wind, *indp;
{ char **result; registerint 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 skippedbytheloopabove),useitandanyadjacentdelimitersto makeaseparatefield.Anyadjacentwhitespacewillbeskippedthe
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 *)));
/* Return an array of tokens, much as the shell might. The tokens are
parsed out of STRING. */ char **
history_tokenize (string) constchar *string;
{ return (history_tokenize_internal (string, -1, (int *)NULL));
}
/* Find and return the word which contains the character at index IND inthehistorylineLINE.Usedtosavethewordmatchedbythe
last history !?string? search. */ staticchar *
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) return ((char *)NULL);
s = words[wind]; for (i = 0; i < wind; i++)
free (words[i]); for (i = wind + 1; words[i]; i++)
free (words[i]);
free (words); return s;
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.46Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.