diff options
author | Chet Ramey <chet.ramey@case.edu> | 2011-12-12 22:03:58 -0500 |
---|---|---|
committer | Chet Ramey <chet.ramey@case.edu> | 2011-12-12 22:03:58 -0500 |
commit | 5f8cde236aa72470a886bf8e7d8aaca32506d8dd (patch) | |
tree | 6472b60e768f40b20cac3c3da60ffea9d3cb26cb | |
parent | 9ec5ed66405529e0d4d7edc5636c692e75edfccd (diff) | |
download | bash-5f8cde236aa72470a886bf8e7d8aaca32506d8dd.tar.gz |
commit bash-20100728 snapshot
79 files changed, 37355 insertions, 3795 deletions
diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index ac3acbd2..bf7abe20 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -10206,7 +10206,8 @@ execute_cmd.c subst.c - when performing pattern substitution word expansions, a `&' in the replacement string is replaced by the text matched by the pattern. - The `&' can be quoted with a backslash to inhibit the expansion + The `&' can be quoted with a backslash to inhibit the expansion. + CURRENTLY DISABLED 7/13 ---- @@ -10248,3 +10249,113 @@ lib/readline/terminal.c ---- lib/readline/xfree.c - new file, contains definition of xfree moved from xmalloc.c + + 7/28 + ---- +variables.c + - check suspect return values from bind_variable before trying to use + the returned SHELL_VAR *. Changes to: initialize_shell_variables, + bind_int_variable, FIND_OR_MAKE_VARIABLE. Fixes bug reported by + Roman Rakus <rrakus@redhat.com> + + 7/31 + ---- +lib/readline/rltty.c + - fix rl_prep_terminal and rl_deprep_terminal to use fileno(stdout) + if rl_instream is NULL. Fixes bug reported by Otto Allmendinger + otto.allmendinger@googlemail.com + + 8/2 + --- +lib/sh/casemod.c + - if the passed string is NULL or empty, return it immediately. Fixes + bug reported by Dennis Williamson <dennistwilliamson@gmail.com> + +subst.c + - fix pat_subst to cope with the passed string being NULL + +arrayfunc.h + - added flag values for array_value_internal and its callers; converted + array_value_internal `allow_all' parameter into a general flags word + - get_array_value now takes a flags value + - changed array_value internal to use *indp as an index to use if the + AV_USEIND flag is set, rather than recomputing it + +subst.c + - get_var_and_type takes two new parameters: a flags word and an index + that represents an already-computed index for an array reference + (just indexed arrays so far). Index is used and passed to array_value + if flags includes AV_USEIND + - parameter_brace_expand_word takes a new argument: the already- + computed index; returns W_ARRAYIND if word expanded is being used + as an array index + - changed parameter_brace_casemod, parameter_brace_patsub, + parameter_brace_substring, parameter_brace_remove_pattern to take new + flags and index arguments from parameter_brace_expand_word. They + pass the new parameters along to get_var_and_type to use an + already-computed array index if necessary. Fixes bug where array + indexes are computed twice reported by Andrew Benton + <b3nton@gmail.com> + +doc/bash.1,lib/readline/doc/{history.3,hsuser.texi} + - modified description of history event designators to clarify that + all non-absolute event designators are relative to the current + position in the history list. Question raised by Frank + Heckenbach <f.heckenbach@fh-soft.de> as debian bash bug 590012 + + 8/5 + --- +subst.c + - remove code that does not add a quoted null when the input string + is partially quoted; subsequent word splitting may require it. + Fixes bug reported by Eric Blake <eblake@redhat.com> + + 8/12 + ---- +lib/glob/gmisc.c + - move match_pattern_wchar and match_pattern_char to new file in + glob library + - new functions: wmatchlen(pat, max) and umatchlen(pat, max), computes + number of characters PAT will match. Returns the number of chars + that will be matched or -1 if the match length is indeterminate + (i.e., contains a `*') + +subst.c + - use umatchlen/wmatchlen in match_upattern/match_wpattern to bound + the number of match attempts in large strings to (usually) one, + depending on match length. Fixes performance problems with + pattern substitution in large strings noted by Yi Yan + <yiyan97@hotmail.com>. Can be applied to remove_[uw]pattern also + + 8/13 + ---- +bashhist.c + - in maybe_append_history, change check for history_lines_this_session + so that we append the lines to the file if it's equal to the value + returned by where_history(). This means that without this change, + the history won't be appended if all the lines in the history list + were added in the current session since the last time the history + file was read or written. Fixes bug reported by Bruce Korb + <bruce.korb@gmail.com> + +shell.h,parse.y + - add prompt_string_pointer to the parser_state struct saved and + restored by {save,restore}_parser_state. Fixes both bugs exposed + by bash_completion and completion of open backquotes reported by + Egmont Koblinger <egmont@gmail.com> + +subst.h + - new flag for skip_to_delim: SD_EXTGLOB. Skip extended globbing + patterns while looking for ending delimiter + +subst.c + - when passed the SD_EXTGLOB flag, skip_to_delim skips over extended + globbing patterns (when extended_glob is set) while looking for a + character in the delimiter set + +pathexp.c + - split_ignorespec: new function to replace calls to extract_colon_unit + in setup_ignore_patterns. uses skip_to_delim with the SD_EXTGLOB + flag to skip over extended globbing patterns in variables like + HISTIGNORE and GLOBIGNORE. Fixes bug reported by Dimitar DIMITROV + <mitkofr@yahoo.fr> and Greg Wooledge <wooledg@eeg.ccf.org> diff --git a/CWRU/CWRU.chlog~ b/CWRU/CWRU.chlog~ index fb2bdb6b..f0e3ad65 100644 --- a/CWRU/CWRU.chlog~ +++ b/CWRU/CWRU.chlog~ @@ -10206,7 +10206,8 @@ execute_cmd.c subst.c - when performing pattern substitution word expansions, a `&' in the replacement string is replaced by the text matched by the pattern. - The `&' can be quoted with a backslash to inhibit the expansion + The `&' can be quoted with a backslash to inhibit the expansion. + CURRENTLY DISABLED 7/13 ---- @@ -10243,3 +10244,118 @@ lib/readline/terminal.c - change rl_resize_terminal to always fetch the new terminal size and only force the redisplay if _rl_echoing_p is non-zero. Fixes bug reported by Balazs Kezes <rlblaster@gmail.com> + + 7/25 + ---- +lib/readline/xfree.c + - new file, contains definition of xfree moved from xmalloc.c + + 7/28 + ---- +variables.c + - check suspect return values from bind_variable before trying to use + the returned SHELL_VAR *. Changes to: initialize_shell_variables, + bind_int_variable, FIND_OR_MAKE_VARIABLE. Fixes bug reported by + Roman Rakus <rrakus@redhat.com> + + 7/31 + ---- +lib/readline/rltty.c + - fix rl_prep_terminal and rl_deprep_terminal to use fileno(stdout) + if rl_instream is NULL. Fixes bug reported by Otto Allmendinger + otto.allmendinger@googlemail.com + + 8/2 + --- +lib/sh/casemod.c + - if the passed string is NULL or empty, return it immediately. Fixes + bug reported by Dennis Williamson <dennistwilliamson@gmail.com> + +subst.c + - fix pat_subst to cope with the passed string being NULL + +arrayfunc.h + - added flag values for array_value_internal and its callers; converted + array_value_internal `allow_all' parameter into a general flags word + - get_array_value now takes a flags value + - changed array_value internal to use *indp as an index to use if the + AV_USEIND flag is set, rather than recomputing it + +subst.c + - get_var_and_type takes two new parameters: a flags word and an index + that represents an already-computed index for an array reference + (just indexed arrays so far). Index is used and passed to array_value + if flags includes AV_USEIND + - parameter_brace_expand_word takes a new argument: the already- + computed index; returns W_ARRAYIND if word expanded is being used + as an array index + - changed parameter_brace_casemod, parameter_brace_patsub, + parameter_brace_substring, parameter_brace_remove_pattern to take new + flags and index arguments from parameter_brace_expand_word. They + pass the new parameters along to get_var_and_type to use an + already-computed array index if necessary. Fixes bug where array + indexes are computed twice reported by Andrew Benton + <b3nton@gmail.com> + +doc/bash.1,lib/readline/doc/{history.3,hsuser.texi} + - modified description of history event designators to clarify that + all non-absolute event designators are relative to the current + position in the history list. Question raised by Frank + Heckenbach <f.heckenbach@fh-soft.de> as debian bash bug 590012 + + 8/5 + --- +subst.c + - remove code that does not add a quoted null when the input string + is partially quoted; subsequent word splitting may require it. + Fixes bug reported by Eric Blake <eblake@redhat.com> + + 8/12 + ---- +lib/glob/gmisc.c + - move match_pattern_wchar and match_pattern_char to new file in + glob library + - new functions: wmatchlen(pat, max) and umatchlen(pat, max), computes + number of characters PAT will match. Returns the number of chars + that will be matched or -1 if the match length is indeterminate + (i.e., contains a `*') + +subst.c + - use umatchlen/wmatchlen in match_upattern/match_wpattern to bound + the number of match attempts in large strings to (usually) one, + depending on match length. Fixes performance problems with + pattern substitution in large strings noted by Yi Yan + <yiyan97@hotmail.com>. Can be applied to remove_[uw]pattern also + + 8/13 + ---- +bashhist.c + - in maybe_append_history, change check for history_lines_this_session + so that we append the lines to the file if it's equal to the value + returned by where_history(). This means that without this change, + the history won't be appended if all the lines in the history list + were added in the current session since the last time the history + file was read or written. Fixes bug reported by Bruce Korb + <bruce.korb@gmail.com> + +shell.h,parse.y + - add prompt_string_pointer to the parser_state struct saved and + restored by {save,restore}_parser_state. Fixes part of bug exposed + by bash_completion and completion of open backquotes reported by + Egmont Koblinger <egmont@gmail.com> + +subst.h + - new flag for skip_to_delim: SD_EXTGLOB. Skip extended globbing + patterns while looking for ending delimiter + +subst.c + - when passed the SD_EXTGLOB flag, skip_to_delim skips over extended + globbing patterns (when extended_glob is set) while looking for a + character in the delimiter set + +pathexp.c + - split_ignorespec: new function to replace calls to extract_colon_unit + in setup_ignore_patterns. uses skip_to_delim with the SD_EXTGLOB + flag to skip over extended globbing patterns in variables like + HISTIGNORE and GLOBIGNORE. Fixes bug reported by Dimitar DIMITROV + <mitkofr@yahoo.fr> and Greg Wooledge <wooledg@eeg.ccf.org> @@ -234,6 +234,7 @@ lib/glob/strmatch.h f lib/glob/glob.c f lib/glob/glob.h f lib/glob/glob_loop.c f +lib/glob/gmisc.c f lib/glob/xmbsrtowcs.c f lib/glob/collsyms.h f lib/glob/doc/Makefile f @@ -763,6 +764,7 @@ tests/array6.sub f tests/array7.sub f tests/array8.sub f tests/array9.sub f +tests/array10.sub f tests/array-at-star f tests/array2.right f tests/assoc.tests f @@ -927,6 +929,7 @@ tests/new-exp4.sub f tests/new-exp5.sub f tests/new-exp6.sub f tests/new-exp7.sub f +tests/new-exp8.sub f tests/new-exp.right f tests/nquote.tests f tests/nquote.right f @@ -234,6 +234,7 @@ lib/glob/strmatch.h f lib/glob/glob.c f lib/glob/glob.h f lib/glob/glob_loop.c f +lib/glob/gmisc.c f lib/glob/xmbsrtowcs.c f lib/glob/collsyms.h f lib/glob/doc/Makefile f @@ -315,6 +316,7 @@ lib/readline/funmap.c f lib/readline/keymaps.c f lib/readline/util.c f lib/readline/terminal.c f +lib/readline/xfree.c f lib/readline/xmalloc.c f lib/readline/search.c f lib/readline/isearch.c f @@ -762,6 +764,7 @@ tests/array6.sub f tests/array7.sub f tests/array8.sub f tests/array9.sub f +tests/array10.sub f tests/array-at-star f tests/array2.right f tests/assoc.tests f diff --git a/Makefile.in b/Makefile.in index 355d8ae9..4b7fd169 100644 --- a/Makefile.in +++ b/Makefile.in @@ -319,9 +319,11 @@ GLOB_DEP = $(GLOB_LIBRARY) GLOB_SOURCE = $(GLOB_LIBSRC)/glob.c $(GLOB_LIBSRC)/strmatch.c \ $(GLOB_LIBSRC)/smatch.c $(GLOB_LIBSRC)/xmbsrtowcs.c \ $(GLOB_LIBSRC)/glob_loop.c $(GLOB_LIBSRC)/sm_loop.c \ + $(GLOB_LIBSRC)/gmisc.c \ $(GLOB_LIBSRC)/glob.h $(GLOB_LIBSRC)/strmatch.h GLOB_OBJ = $(GLOB_LIBDIR)/glob.o $(GLOB_LIBDIR)/strmatch.o \ - $(GLOB_LIBDIR)/smatch.o $(GLOB_LIBDIR)/xmbsrtowcs.o + $(GLOB_LIBDIR)/smatch.o $(GLOB_LIBDIR)/xmbsrtowcs.o \ + $(GLOB_LIBDIR)/gmisc.o # The source, object and documentation for the GNU Tilde library. TILDE_LIBSRC = $(LIBSRC)/tilde diff --git a/Makefile.in~ b/Makefile.in~ index 1444f47f..355d8ae9 100644 --- a/Makefile.in~ +++ b/Makefile.in~ @@ -565,7 +565,7 @@ strip: $(Program) .made lint: ${MAKE} ${MFLAGS} CFLAGS='${GCC_LINT_FLAGS}' .made -version.h: $(SOURCES) config.h Makefile +version.h: $(SOURCES) config.h Makefile patchlevel.h $(SHELL) $(SUPPORT_SRC)mkversion.sh -b -S ${topdir} -s $(RELSTATUS) -d $(Version) -o newversion.h \ && mv newversion.h version.h diff --git a/arrayfunc.c b/arrayfunc.c index df4d2ce1..21eb4cd7 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -864,9 +864,9 @@ array_variable_part (s, subp, lenp) is non-null it gets 1 if the array reference is name[*], 2 if the reference is name[@], and 0 otherwise. */ static char * -array_value_internal (s, quoted, allow_all, rtype, indp) +array_value_internal (s, quoted, flags, rtype, indp) char *s; - int quoted, allow_all, *rtype; + int quoted, flags, *rtype; arrayind_t *indp; { int len; @@ -893,7 +893,7 @@ array_value_internal (s, quoted, allow_all, rtype, indp) { if (rtype) *rtype = (t[0] == '*') ? 1 : 2; - if (allow_all == 0) + if ((flags & AV_ALLOWALL) == 0) { err_badarraysub (s); return ((char *)NULL); @@ -932,17 +932,22 @@ array_value_internal (s, quoted, allow_all, rtype, indp) *rtype = 0; if (var == 0 || array_p (var) || assoc_p (var) == 0) { - ind = array_expand_index (t, len); - if (ind < 0) + if ((flags & AV_USEIND) == 0 || indp == 0) { - /* negative subscripts to indexed arrays count back from end */ - if (var && array_p (var)) - ind = array_max_index (array_cell (var)) + 1 + ind; + ind = array_expand_index (t, len); if (ind < 0) - INDEX_ERROR(); + { + /* negative subscripts to indexed arrays count back from end */ + if (var && array_p (var)) + ind = array_max_index (array_cell (var)) + 1 + ind; + if (ind < 0) + INDEX_ERROR(); + } + if (indp) + *indp = ind; } - if (indp) - *indp = ind; + else if (indp) + ind = *indp; } else if (assoc_p (var)) { @@ -972,25 +977,25 @@ array_value_internal (s, quoted, allow_all, rtype, indp) /* Return a string containing the elements described by the array and subscript contained in S, obeying quoting for subscripts * and @. */ char * -array_value (s, quoted, rtype, indp) +array_value (s, quoted, flags, rtype, indp) char *s; - int quoted, *rtype; + int quoted, flags, *rtype; arrayind_t *indp; { - return (array_value_internal (s, quoted, 1, rtype, indp)); + return (array_value_internal (s, quoted, flags|AV_ALLOWALL, rtype, indp)); } /* Return the value of the array indexing expression S as a single string. - If ALLOW_ALL is 0, do not allow `@' and `*' subscripts. This is used - by other parts of the shell such as the arithmetic expression evaluator - in expr.c. */ + If (FLAGS & AV_ALLOWALL) is 0, do not allow `@' and `*' subscripts. This + is used by other parts of the shell such as the arithmetic expression + evaluator in expr.c. */ char * -get_array_value (s, allow_all, rtype, indp) +get_array_value (s, flags, rtype, indp) char *s; - int allow_all, *rtype; + int flags, *rtype; arrayind_t *indp; { - return (array_value_internal (s, 0, allow_all, rtype, indp)); + return (array_value_internal (s, 0, flags, rtype, indp)); } char * diff --git a/arrayfunc.c~ b/arrayfunc.c~ index c0b7b542..337d3439 100644 --- a/arrayfunc.c~ +++ b/arrayfunc.c~ @@ -864,9 +864,9 @@ array_variable_part (s, subp, lenp) is non-null it gets 1 if the array reference is name[*], 2 if the reference is name[@], and 0 otherwise. */ static char * -array_value_internal (s, quoted, allow_all, rtype, indp) +array_value_internal (s, quoted, flags, rtype, indp) char *s; - int quoted, allow_all, *rtype; + int quoted, flags, *rtype; arrayind_t *indp; { int len; @@ -893,7 +893,7 @@ array_value_internal (s, quoted, allow_all, rtype, indp) { if (rtype) *rtype = (t[0] == '*') ? 1 : 2; - if (allow_all == 0) + if ((flags & AV_ALLOWALL) == 0) { err_badarraysub (s); return ((char *)NULL); @@ -932,16 +932,22 @@ array_value_internal (s, quoted, allow_all, rtype, indp) *rtype = 0; if (var == 0 || array_p (var) || assoc_p (var) == 0) { - ind = array_expand_index (t, len); - if (ind < 0) + if ((flags & AV_USEIND) == 0) { - if (var && array_p (var)) - ind = array_max_index (array_cell (var)) + 1 + ind; + ind = array_expand_index (t, len); if (ind < 0) - INDEX_ERROR(); + { + /* negative subscripts to indexed arrays count back from end */ + if (var && array_p (var)) + ind = array_max_index (array_cell (var)) + 1 + ind; + if (ind < 0) + INDEX_ERROR(); + } + if (indp) + *indp = ind; } - if (indp) - *indp = ind; + else if (indp) + ind = *indp; } else if (assoc_p (var)) { @@ -971,25 +977,25 @@ array_value_internal (s, quoted, allow_all, rtype, indp) /* Return a string containing the elements described by the array and subscript contained in S, obeying quoting for subscripts * and @. */ char * -array_value (s, quoted, rtype, indp) +array_value (s, quoted, flags, rtype, indp) char *s; - int quoted, *rtype; + int quoted, flags, *rtype; arrayind_t *indp; { - return (array_value_internal (s, quoted, 1, rtype, indp)); + return (array_value_internal (s, quoted, flags|AV_ALLOWALL, rtype, indp)); } /* Return the value of the array indexing expression S as a single string. - If ALLOW_ALL is 0, do not allow `@' and `*' subscripts. This is used - by other parts of the shell such as the arithmetic expression evaluator - in expr.c. */ + If (FLAGS & AV_ALLOWALL) is 0, do not allow `@' and `*' subscripts. This + is used by other parts of the shell such as the arithmetic expression + evaluator in expr.c. */ char * -get_array_value (s, allow_all, rtype, indp) +get_array_value (s, flags, rtype, indp) char *s; - int allow_all, *rtype; + int flags, *rtype; arrayind_t *indp; { - return (array_value_internal (s, 0, allow_all, rtype, indp)); + return (array_value_internal (s, 0, flags, rtype, indp)); } char * diff --git a/arrayfunc.h b/arrayfunc.h index 286dd8c4..8363d16b 100644 --- a/arrayfunc.h +++ b/arrayfunc.h @@ -25,6 +25,11 @@ #if defined (ARRAY_VARS) +/* Flags for array_value_internal and callers array_value/get_array_value */ +#define AV_ALLOWALL 0x001 +#define AV_QUOTED 0x002 +#define AV_USEIND 0x004 + extern SHELL_VAR *convert_var_to_array __P((SHELL_VAR *)); extern SHELL_VAR *convert_var_to_assoc __P((SHELL_VAR *)); @@ -51,7 +56,7 @@ extern void print_assoc_assignment __P((SHELL_VAR *, int)); extern arrayind_t array_expand_index __P((char *, int)); extern int valid_array_reference __P((char *)); -extern char *array_value __P((char *, int, int *, arrayind_t *)); +extern char *array_value __P((char *, int, int, int *, arrayind_t *)); extern char *get_array_value __P((char *, int, int *, arrayind_t *)); extern char *array_keys __P((char *, int)); diff --git a/arrayfunc.h~ b/arrayfunc.h~ index 9add1189..6e8851ff 100644 --- a/arrayfunc.h~ +++ b/arrayfunc.h~ @@ -1,6 +1,6 @@ /* arrayfunc.h -- declarations for miscellaneous array functions in arrayfunc.c */ -/* Copyright (C) 2001-2009 Free Software Foundation, Inc. +/* Copyright (C) 2001-2010 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -25,6 +25,11 @@ #if defined (ARRAY_VARS) +/* Flags for array_value_internal and callers */ +#define AV_ALLOWALL 0x001 +#define AV_QUOTED 0x002 +#define AV_USEIND 0x004 + extern SHELL_VAR *convert_var_to_array __P((SHELL_VAR *)); extern SHELL_VAR *convert_var_to_assoc __P((SHELL_VAR *)); diff --git a/autom4te.cache/output.0 b/autom4te.cache/output.0 index c5295b74..c6fcfc4c 100644 --- a/autom4te.cache/output.0 +++ b/autom4te.cache/output.0 @@ -27176,6 +27176,366 @@ _ACEOF fi +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:$LINENO: checking size of intmax_t" >&5 +$as_echo_n "checking size of intmax_t... " >&6; } +if test "${ac_cv_sizeof_intmax_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array @<:@1 - 2 * !(((long int) (sizeof (intmax_t))) >= 0)@:>@; +test_array @<:@0@:>@ = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array @<:@1 - 2 * !(((long int) (sizeof (intmax_t))) <= $ac_mid)@:>@; +test_array @<:@0@:>@ = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_hi=$ac_mid; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array @<:@1 - 2 * !(((long int) (sizeof (intmax_t))) < 0)@:>@; +test_array @<:@0@:>@ = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array @<:@1 - 2 * !(((long int) (sizeof (intmax_t))) >= $ac_mid)@:>@; +test_array @<:@0@:>@ = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_lo=$ac_mid; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_lo= ac_hi= +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array @<:@1 - 2 * !(((long int) (sizeof (intmax_t))) <= $ac_mid)@:>@; +test_array @<:@0@:>@ = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_hi=$ac_mid +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_lo=`expr '(' $ac_mid ')' + 1` +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_intmax_t=$ac_lo;; +'') if test "$ac_cv_type_intmax_t" = yes; then + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute sizeof (intmax_t) +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute sizeof (intmax_t) +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } + else + ac_cv_sizeof_intmax_t=0 + fi ;; +esac +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +static long int longval () { return (long int) (sizeof (intmax_t)); } +static unsigned long int ulongval () { return (long int) (sizeof (intmax_t)); } +@%:@include <stdio.h> +@%:@include <stdlib.h> +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (((long int) (sizeof (intmax_t))) < 0) + { + long int i = longval (); + if (i != ((long int) (sizeof (intmax_t)))) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ((long int) (sizeof (intmax_t)))) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_intmax_t=`cat conftest.val` +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +if test "$ac_cv_type_intmax_t" = yes; then + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute sizeof (intmax_t) +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute sizeof (intmax_t) +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } + else + ac_cv_sizeof_intmax_t=0 + fi +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.val +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_sizeof_intmax_t" >&5 +$as_echo "$ac_cv_sizeof_intmax_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +@%:@define SIZEOF_INTMAX_T $ac_cv_sizeof_intmax_t +_ACEOF + + + { $as_echo "$as_me:$LINENO: checking for struct termios.c_line" >&5 $as_echo_n "checking for struct termios.c_line... " >&6; } diff --git a/autom4te.cache/requests b/autom4te.cache/requests index 014c1526..4a5e08c9 100644 --- a/autom4te.cache/requests +++ b/autom4te.cache/requests @@ -15,25 +15,25 @@ 'configure.in' ], { - 'AM_PROG_F77_C_O' => 1, '_LT_AC_TAGCONFIG' => 1, - 'm4_pattern_forbid' => 1, + 'AM_PROG_F77_C_O' => 1, 'AC_INIT' => 1, - 'AC_CANONICAL_TARGET' => 1, + 'm4_pattern_forbid' => 1, '_AM_COND_IF' => 1, - 'AC_CONFIG_LIBOBJ_DIR' => 1, + 'AC_CANONICAL_TARGET' => 1, 'AC_SUBST' => 1, - 'AC_CANONICAL_HOST' => 1, + 'AC_CONFIG_LIBOBJ_DIR' => 1, 'AC_FC_SRCEXT' => 1, + 'AC_CANONICAL_HOST' => 1, 'AC_PROG_LIBTOOL' => 1, 'AM_INIT_AUTOMAKE' => 1, 'AC_CONFIG_SUBDIRS' => 1, 'AM_AUTOMAKE_VERSION' => 1, 'LT_CONFIG_LTDL_DIR' => 1, - 'AC_CONFIG_LINKS' => 1, 'AC_REQUIRE_AUX_FILE' => 1, - 'LT_SUPPORTED_TAG' => 1, + 'AC_CONFIG_LINKS' => 1, 'm4_sinclude' => 1, + 'LT_SUPPORTED_TAG' => 1, 'AM_MAINTAINER_MODE' => 1, 'AM_GNU_GETTEXT_INTL_SUBDIR' => 1, '_m4_warn' => 1, @@ -49,13 +49,13 @@ 'AC_CANONICAL_BUILD' => 1, 'AC_FC_FREEFORM' => 1, 'AH_OUTPUT' => 1, - 'AC_CONFIG_AUX_DIR' => 1, '_AM_SUBST_NOTMAKE' => 1, - 'AM_PROG_CC_C_O' => 1, - 'm4_pattern_allow' => 1, + 'AC_CONFIG_AUX_DIR' => 1, 'sinclude' => 1, - 'AM_CONDITIONAL' => 1, + 'm4_pattern_allow' => 1, + 'AM_PROG_CC_C_O' => 1, 'AC_CANONICAL_SYSTEM' => 1, + 'AM_CONDITIONAL' => 1, 'AC_CONFIG_HEADERS' => 1, 'AC_DEFINE_TRACE_LITERAL' => 1, 'm4_include' => 1, diff --git a/autom4te.cache/traces.0 b/autom4te.cache/traces.0 index 20d34e6b..4fbd6ca6 100644 --- a/autom4te.cache/traces.0 +++ b/autom4te.cache/traces.0 @@ -2149,41 +2149,45 @@ m4trace:configure.in:906: -1- AC_DEFINE_TRACE_LITERAL([RLIMTYPE]) m4trace:configure.in:906: -1- m4_pattern_allow([^RLIMTYPE$]) m4trace:configure.in:906: -1- AC_DEFINE_TRACE_LITERAL([RLIMTYPE]) m4trace:configure.in:906: -1- m4_pattern_allow([^RLIMTYPE$]) -m4trace:configure.in:909: -2- AC_DEFINE_TRACE_LITERAL([TERMIOS_LDISC]) -m4trace:configure.in:909: -2- m4_pattern_allow([^TERMIOS_LDISC$]) -m4trace:configure.in:910: -2- AC_DEFINE_TRACE_LITERAL([TERMIO_LDISC]) -m4trace:configure.in:910: -2- m4_pattern_allow([^TERMIO_LDISC$]) -m4trace:configure.in:911: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +m4trace:configure.in:908: -1- AC_DEFINE_TRACE_LITERAL([SIZEOF_INTMAX_T]) +m4trace:configure.in:908: -1- m4_pattern_allow([^SIZEOF_INTMAX_T$]) +m4trace:configure.in:908: -1- AH_OUTPUT([SIZEOF_INTMAX_T], [/* The size of `intmax_t\', as computed by sizeof. */ +#undef SIZEOF_INTMAX_T]) +m4trace:configure.in:911: -2- AC_DEFINE_TRACE_LITERAL([TERMIOS_LDISC]) +m4trace:configure.in:911: -2- m4_pattern_allow([^TERMIOS_LDISC$]) +m4trace:configure.in:912: -2- AC_DEFINE_TRACE_LITERAL([TERMIO_LDISC]) +m4trace:configure.in:912: -2- m4_pattern_allow([^TERMIO_LDISC$]) +m4trace:configure.in:913: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1042: BASH_STRUCT_DIRENT_D_INO is expanded from... -configure.in:911: the top level]) -m4trace:configure.in:911: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_DIRENT_D_INO]) -m4trace:configure.in:911: -1- m4_pattern_allow([^HAVE_STRUCT_DIRENT_D_INO$]) -m4trace:configure.in:912: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +configure.in:913: the top level]) +m4trace:configure.in:913: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_DIRENT_D_INO]) +m4trace:configure.in:913: -1- m4_pattern_allow([^HAVE_STRUCT_DIRENT_D_INO$]) +m4trace:configure.in:914: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1075: BASH_STRUCT_DIRENT_D_FILENO is expanded from... -configure.in:912: the top level]) -m4trace:configure.in:912: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_DIRENT_D_FILENO]) -m4trace:configure.in:912: -1- m4_pattern_allow([^HAVE_STRUCT_DIRENT_D_FILENO$]) -m4trace:configure.in:913: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +configure.in:914: the top level]) +m4trace:configure.in:914: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_DIRENT_D_FILENO]) +m4trace:configure.in:914: -1- m4_pattern_allow([^HAVE_STRUCT_DIRENT_D_FILENO$]) +m4trace:configure.in:915: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1108: BASH_STRUCT_DIRENT_D_NAMLEN is expanded from... -configure.in:913: the top level]) -m4trace:configure.in:913: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_DIRENT_D_NAMLEN]) -m4trace:configure.in:913: -1- m4_pattern_allow([^HAVE_STRUCT_DIRENT_D_NAMLEN$]) -m4trace:configure.in:914: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +configure.in:915: the top level]) +m4trace:configure.in:915: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_DIRENT_D_NAMLEN]) +m4trace:configure.in:915: -1- m4_pattern_allow([^HAVE_STRUCT_DIRENT_D_NAMLEN$]) +m4trace:configure.in:916: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1173: BASH_STRUCT_WINSIZE is expanded from... -configure.in:914: the top level]) -m4trace:configure.in:914: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +configure.in:916: the top level]) +m4trace:configure.in:916: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:2462: AC_COMPILE_IFELSE is expanded from... @@ -2191,255 +2195,255 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1173: BASH_STRUCT_WINSIZE is expanded from... -configure.in:914: the top level]) -m4trace:configure.in:914: -1- AC_DEFINE_TRACE_LITERAL([STRUCT_WINSIZE_IN_SYS_IOCTL]) -m4trace:configure.in:914: -1- m4_pattern_allow([^STRUCT_WINSIZE_IN_SYS_IOCTL$]) -m4trace:configure.in:914: -1- AC_DEFINE_TRACE_LITERAL([STRUCT_WINSIZE_IN_TERMIOS]) -m4trace:configure.in:914: -1- m4_pattern_allow([^STRUCT_WINSIZE_IN_TERMIOS$]) -m4trace:configure.in:915: -1- AC_DEFINE_TRACE_LITERAL([HAVE_TIMEVAL]) -m4trace:configure.in:915: -1- m4_pattern_allow([^HAVE_TIMEVAL$]) -m4trace:configure.in:916: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_STAT_ST_BLOCKS]) -m4trace:configure.in:916: -1- m4_pattern_allow([^HAVE_STRUCT_STAT_ST_BLOCKS$]) -m4trace:configure.in:916: -1- AH_OUTPUT([HAVE_STRUCT_STAT_ST_BLOCKS], [/* Define to 1 if `st_blocks\' is member of `struct stat\'. */ +configure.in:916: the top level]) +m4trace:configure.in:916: -1- AC_DEFINE_TRACE_LITERAL([STRUCT_WINSIZE_IN_SYS_IOCTL]) +m4trace:configure.in:916: -1- m4_pattern_allow([^STRUCT_WINSIZE_IN_SYS_IOCTL$]) +m4trace:configure.in:916: -1- AC_DEFINE_TRACE_LITERAL([STRUCT_WINSIZE_IN_TERMIOS]) +m4trace:configure.in:916: -1- m4_pattern_allow([^STRUCT_WINSIZE_IN_TERMIOS$]) +m4trace:configure.in:917: -1- AC_DEFINE_TRACE_LITERAL([HAVE_TIMEVAL]) +m4trace:configure.in:917: -1- m4_pattern_allow([^HAVE_TIMEVAL$]) +m4trace:configure.in:918: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_STAT_ST_BLOCKS]) +m4trace:configure.in:918: -1- m4_pattern_allow([^HAVE_STRUCT_STAT_ST_BLOCKS$]) +m4trace:configure.in:918: -1- AH_OUTPUT([HAVE_STRUCT_STAT_ST_BLOCKS], [/* Define to 1 if `st_blocks\' is member of `struct stat\'. */ #undef HAVE_STRUCT_STAT_ST_BLOCKS]) -m4trace:configure.in:917: -1- AC_DEFINE_TRACE_LITERAL([TM_IN_SYS_TIME]) -m4trace:configure.in:917: -1- m4_pattern_allow([^TM_IN_SYS_TIME$]) -m4trace:configure.in:917: -1- AH_OUTPUT([TM_IN_SYS_TIME], [/* Define to 1 if your <sys/time.h> declares `struct tm\'. */ +m4trace:configure.in:919: -1- AC_DEFINE_TRACE_LITERAL([TM_IN_SYS_TIME]) +m4trace:configure.in:919: -1- m4_pattern_allow([^TM_IN_SYS_TIME$]) +m4trace:configure.in:919: -1- AH_OUTPUT([TM_IN_SYS_TIME], [/* Define to 1 if your <sys/time.h> declares `struct tm\'. */ #undef TM_IN_SYS_TIME]) -m4trace:configure.in:918: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_TM_TM_ZONE]) -m4trace:configure.in:918: -1- m4_pattern_allow([^HAVE_STRUCT_TM_TM_ZONE$]) -m4trace:configure.in:918: -1- AH_OUTPUT([HAVE_STRUCT_TM_TM_ZONE], [/* Define to 1 if `tm_zone\' is member of `struct tm\'. */ +m4trace:configure.in:920: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_TM_TM_ZONE]) +m4trace:configure.in:920: -1- m4_pattern_allow([^HAVE_STRUCT_TM_TM_ZONE$]) +m4trace:configure.in:920: -1- AH_OUTPUT([HAVE_STRUCT_TM_TM_ZONE], [/* Define to 1 if `tm_zone\' is member of `struct tm\'. */ #undef HAVE_STRUCT_TM_TM_ZONE]) -m4trace:configure.in:918: -1- AC_DEFINE_TRACE_LITERAL([HAVE_TM_ZONE]) -m4trace:configure.in:918: -1- m4_pattern_allow([^HAVE_TM_ZONE$]) -m4trace:configure.in:918: -1- AH_OUTPUT([HAVE_TM_ZONE], [/* Define to 1 if your `struct tm\' has `tm_zone\'. Deprecated, use +m4trace:configure.in:920: -1- AC_DEFINE_TRACE_LITERAL([HAVE_TM_ZONE]) +m4trace:configure.in:920: -1- m4_pattern_allow([^HAVE_TM_ZONE$]) +m4trace:configure.in:920: -1- AH_OUTPUT([HAVE_TM_ZONE], [/* Define to 1 if your `struct tm\' has `tm_zone\'. Deprecated, use `HAVE_STRUCT_TM_TM_ZONE\' instead. */ #undef HAVE_TM_ZONE]) -m4trace:configure.in:918: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_TZNAME]) -m4trace:configure.in:918: -1- m4_pattern_allow([^HAVE_DECL_TZNAME$]) -m4trace:configure.in:918: -1- AH_OUTPUT([HAVE_DECL_TZNAME], [/* Define to 1 if you have the declaration of `tzname\', and to 0 if you don\'t. +m4trace:configure.in:920: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_TZNAME]) +m4trace:configure.in:920: -1- m4_pattern_allow([^HAVE_DECL_TZNAME$]) +m4trace:configure.in:920: -1- AH_OUTPUT([HAVE_DECL_TZNAME], [/* Define to 1 if you have the declaration of `tzname\', and to 0 if you don\'t. */ #undef HAVE_DECL_TZNAME]) -m4trace:configure.in:918: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_TZNAME]) -m4trace:configure.in:918: -1- m4_pattern_allow([^HAVE_DECL_TZNAME$]) -m4trace:configure.in:918: -1- AC_DEFINE_TRACE_LITERAL([HAVE_TZNAME]) -m4trace:configure.in:918: -1- m4_pattern_allow([^HAVE_TZNAME$]) -m4trace:configure.in:918: -1- AH_OUTPUT([HAVE_TZNAME], [/* Define to 1 if you don\'t have `tm_zone\' but do have the external array +m4trace:configure.in:920: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_TZNAME]) +m4trace:configure.in:920: -1- m4_pattern_allow([^HAVE_DECL_TZNAME$]) +m4trace:configure.in:920: -1- AC_DEFINE_TRACE_LITERAL([HAVE_TZNAME]) +m4trace:configure.in:920: -1- m4_pattern_allow([^HAVE_TZNAME$]) +m4trace:configure.in:920: -1- AH_OUTPUT([HAVE_TZNAME], [/* Define to 1 if you don\'t have `tm_zone\' but do have the external array `tzname\'. */ #undef HAVE_TZNAME]) -m4trace:configure.in:919: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_TIMEZONE]) -m4trace:configure.in:919: -1- m4_pattern_allow([^HAVE_STRUCT_TIMEZONE$]) -m4trace:configure.in:921: -1- AC_DEFINE_TRACE_LITERAL([WEXITSTATUS_OFFSET]) -m4trace:configure.in:921: -1- m4_pattern_allow([^WEXITSTATUS_OFFSET$]) -m4trace:configure.in:921: -1- AH_OUTPUT([WEXITSTATUS_OFFSET], [/* Offset of exit status in wait status word */ +m4trace:configure.in:921: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRUCT_TIMEZONE]) +m4trace:configure.in:921: -1- m4_pattern_allow([^HAVE_STRUCT_TIMEZONE$]) +m4trace:configure.in:923: -1- AC_DEFINE_TRACE_LITERAL([WEXITSTATUS_OFFSET]) +m4trace:configure.in:923: -1- m4_pattern_allow([^WEXITSTATUS_OFFSET$]) +m4trace:configure.in:923: -1- AH_OUTPUT([WEXITSTATUS_OFFSET], [/* Offset of exit status in wait status word */ #undef WEXITSTATUS_OFFSET]) -m4trace:configure.in:924: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete. +m4trace:configure.in:926: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2527: AC_TRY_LINK is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:299: BASH_FUNC_STRSIGNAL is expanded from... -configure.in:924: the top level]) -m4trace:configure.in:924: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRSIGNAL]) -m4trace:configure.in:924: -1- m4_pattern_allow([^HAVE_STRSIGNAL$]) -m4trace:configure.in:925: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:926: the top level]) +m4trace:configure.in:926: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STRSIGNAL]) +m4trace:configure.in:926: -1- m4_pattern_allow([^HAVE_STRSIGNAL$]) +m4trace:configure.in:927: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:313: BASH_FUNC_OPENDIR_CHECK is expanded from... -configure.in:925: the top level]) -m4trace:configure.in:925: -1- AC_DEFINE_TRACE_LITERAL([OPENDIR_NOT_ROBUST]) -m4trace:configure.in:925: -1- m4_pattern_allow([^OPENDIR_NOT_ROBUST$]) -m4trace:configure.in:926: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:927: the top level]) +m4trace:configure.in:927: -1- AC_DEFINE_TRACE_LITERAL([OPENDIR_NOT_ROBUST]) +m4trace:configure.in:927: -1- m4_pattern_allow([^OPENDIR_NOT_ROBUST$]) +m4trace:configure.in:928: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:683: BASH_FUNC_ULIMIT_MAXFDS is expanded from... -configure.in:926: the top level]) -m4trace:configure.in:926: -1- AC_DEFINE_TRACE_LITERAL([ULIMIT_MAXFDS]) -m4trace:configure.in:926: -1- m4_pattern_allow([^ULIMIT_MAXFDS$]) -m4trace:configure.in:927: -1- AH_OUTPUT([HAVE_FPURGE], [/* Define to 1 if you have the `fpurge\' function. */ +configure.in:928: the top level]) +m4trace:configure.in:928: -1- AC_DEFINE_TRACE_LITERAL([ULIMIT_MAXFDS]) +m4trace:configure.in:928: -1- m4_pattern_allow([^ULIMIT_MAXFDS$]) +m4trace:configure.in:929: -1- AH_OUTPUT([HAVE_FPURGE], [/* Define to 1 if you have the `fpurge\' function. */ #undef HAVE_FPURGE]) -m4trace:configure.in:927: -1- AH_OUTPUT([HAVE___FPURGE], [/* Define to 1 if you have the `__fpurge\' function. */ +m4trace:configure.in:929: -1- AH_OUTPUT([HAVE___FPURGE], [/* Define to 1 if you have the `__fpurge\' function. */ #undef HAVE___FPURGE]) -m4trace:configure.in:927: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_FPURGE]) -m4trace:configure.in:927: -1- m4_pattern_allow([^HAVE_DECL_FPURGE$]) -m4trace:configure.in:927: -1- AH_OUTPUT([HAVE_DECL_FPURGE], [/* Define to 1 if you have the declaration of `fpurge\', and to 0 if you don\'t. +m4trace:configure.in:929: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_FPURGE]) +m4trace:configure.in:929: -1- m4_pattern_allow([^HAVE_DECL_FPURGE$]) +m4trace:configure.in:929: -1- AH_OUTPUT([HAVE_DECL_FPURGE], [/* Define to 1 if you have the declaration of `fpurge\', and to 0 if you don\'t. */ #undef HAVE_DECL_FPURGE]) -m4trace:configure.in:927: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_FPURGE]) -m4trace:configure.in:927: -1- m4_pattern_allow([^HAVE_DECL_FPURGE$]) -m4trace:configure.in:928: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +m4trace:configure.in:929: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DECL_FPURGE]) +m4trace:configure.in:929: -1- m4_pattern_allow([^HAVE_DECL_FPURGE$]) +m4trace:configure.in:930: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:579: BASH_FUNC_GETENV is expanded from... -configure.in:928: the top level]) -m4trace:configure.in:928: -1- AC_DEFINE_TRACE_LITERAL([CAN_REDEFINE_GETENV]) -m4trace:configure.in:928: -1- m4_pattern_allow([^CAN_REDEFINE_GETENV$]) -m4trace:configure.in:930: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:930: the top level]) +m4trace:configure.in:930: -1- AC_DEFINE_TRACE_LITERAL([CAN_REDEFINE_GETENV]) +m4trace:configure.in:930: -1- m4_pattern_allow([^CAN_REDEFINE_GETENV$]) +m4trace:configure.in:932: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:702: BASH_FUNC_GETCWD is expanded from... -configure.in:930: the top level]) -m4trace:configure.in:930: -1- AC_DEFINE_TRACE_LITERAL([GETCWD_BROKEN]) -m4trace:configure.in:930: -1- m4_pattern_allow([^GETCWD_BROKEN$]) -m4trace:configure.in:930: -1- AC_LIBSOURCE([getcwd.c]) -m4trace:configure.in:930: -1- AC_SUBST([LIB@&t@OBJS], ["$LIB@&t@OBJS getcwd.$ac_objext"]) -m4trace:configure.in:930: -1- AC_SUBST_TRACE([LIB@&t@OBJS]) -m4trace:configure.in:930: -1- m4_pattern_allow([^LIB@&t@OBJS$]) -m4trace:configure.in:932: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:932: the top level]) +m4trace:configure.in:932: -1- AC_DEFINE_TRACE_LITERAL([GETCWD_BROKEN]) +m4trace:configure.in:932: -1- m4_pattern_allow([^GETCWD_BROKEN$]) +m4trace:configure.in:932: -1- AC_LIBSOURCE([getcwd.c]) +m4trace:configure.in:932: -1- AC_SUBST([LIB@&t@OBJS], ["$LIB@&t@OBJS getcwd.$ac_objext"]) +m4trace:configure.in:932: -1- AC_SUBST_TRACE([LIB@&t@OBJS]) +m4trace:configure.in:932: -1- m4_pattern_allow([^LIB@&t@OBJS$]) +m4trace:configure.in:934: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:778: BASH_FUNC_POSIX_SETJMP is expanded from... -configure.in:932: the top level]) -m4trace:configure.in:932: -1- AC_DEFINE_TRACE_LITERAL([HAVE_POSIX_SIGSETJMP]) -m4trace:configure.in:932: -1- m4_pattern_allow([^HAVE_POSIX_SIGSETJMP$]) -m4trace:configure.in:933: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:934: the top level]) +m4trace:configure.in:934: -1- AC_DEFINE_TRACE_LITERAL([HAVE_POSIX_SIGSETJMP]) +m4trace:configure.in:934: -1- m4_pattern_allow([^HAVE_POSIX_SIGSETJMP$]) +m4trace:configure.in:935: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:829: BASH_FUNC_STRCOLL is expanded from... -configure.in:933: the top level]) -m4trace:configure.in:933: -1- AC_DEFINE_TRACE_LITERAL([STRCOLL_BROKEN]) -m4trace:configure.in:933: -1- m4_pattern_allow([^STRCOLL_BROKEN$]) -m4trace:configure.in:934: -1- AH_OUTPUT([HAVE_SNPRINTF], [/* Define to 1 if you have the `snprintf\' function. */ +configure.in:935: the top level]) +m4trace:configure.in:935: -1- AC_DEFINE_TRACE_LITERAL([STRCOLL_BROKEN]) +m4trace:configure.in:935: -1- m4_pattern_allow([^STRCOLL_BROKEN$]) +m4trace:configure.in:936: -1- AH_OUTPUT([HAVE_SNPRINTF], [/* Define to 1 if you have the `snprintf\' function. */ #undef HAVE_SNPRINTF]) -m4trace:configure.in:934: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +m4trace:configure.in:936: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... ../../lib/autoconf/general.m4:1994: AC_CACHE_CHECK is expanded from... aclocal.m4:4039: BASH_FUNC_SNPRINTF is expanded from... -configure.in:934: the top level]) -m4trace:configure.in:934: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SNPRINTF]) -m4trace:configure.in:934: -1- m4_pattern_allow([^HAVE_SNPRINTF$]) -m4trace:configure.in:934: -1- AH_OUTPUT([HAVE_SNPRINTF], [/* Define if you have a standard-conformant snprintf function. */ +configure.in:936: the top level]) +m4trace:configure.in:936: -1- AC_DEFINE_TRACE_LITERAL([HAVE_SNPRINTF]) +m4trace:configure.in:936: -1- m4_pattern_allow([^HAVE_SNPRINTF$]) +m4trace:configure.in:936: -1- AH_OUTPUT([HAVE_SNPRINTF], [/* Define if you have a standard-conformant snprintf function. */ #undef HAVE_SNPRINTF]) -m4trace:configure.in:935: -1- AH_OUTPUT([HAVE_VSNPRINTF], [/* Define to 1 if you have the `vsnprintf\' function. */ +m4trace:configure.in:937: -1- AH_OUTPUT([HAVE_VSNPRINTF], [/* Define to 1 if you have the `vsnprintf\' function. */ #undef HAVE_VSNPRINTF]) -m4trace:configure.in:935: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +m4trace:configure.in:937: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... ../../lib/autoconf/general.m4:1994: AC_CACHE_CHECK is expanded from... aclocal.m4:4067: BASH_FUNC_VSNPRINTF is expanded from... -configure.in:935: the top level]) -m4trace:configure.in:935: -1- AC_DEFINE_TRACE_LITERAL([HAVE_VSNPRINTF]) -m4trace:configure.in:935: -1- m4_pattern_allow([^HAVE_VSNPRINTF$]) -m4trace:configure.in:935: -1- AH_OUTPUT([HAVE_VSNPRINTF], [/* Define if you have a standard-conformant vsnprintf function. */ +configure.in:937: the top level]) +m4trace:configure.in:937: -1- AC_DEFINE_TRACE_LITERAL([HAVE_VSNPRINTF]) +m4trace:configure.in:937: -1- m4_pattern_allow([^HAVE_VSNPRINTF$]) +m4trace:configure.in:937: -1- AH_OUTPUT([HAVE_VSNPRINTF], [/* Define if you have a standard-conformant vsnprintf function. */ #undef HAVE_VSNPRINTF]) -m4trace:configure.in:941: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete. +m4trace:configure.in:943: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2527: AC_TRY_LINK is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... ../../lib/autoconf/general.m4:1994: AC_CACHE_CHECK is expanded from... aclocal.m4:624: BASH_FUNC_STD_PUTENV is expanded from... -configure.in:941: the top level]) -m4trace:configure.in:941: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STD_PUTENV]) -m4trace:configure.in:941: -1- m4_pattern_allow([^HAVE_STD_PUTENV$]) +configure.in:943: the top level]) m4trace:configure.in:943: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STD_PUTENV]) m4trace:configure.in:943: -1- m4_pattern_allow([^HAVE_STD_PUTENV$]) -m4trace:configure.in:946: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete. +m4trace:configure.in:945: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STD_PUTENV]) +m4trace:configure.in:945: -1- m4_pattern_allow([^HAVE_STD_PUTENV$]) +m4trace:configure.in:948: -1- _m4_warn([obsolete], [The macro `AC_TRY_LINK' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2527: AC_TRY_LINK is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... ../../lib/autoconf/general.m4:1994: AC_CACHE_CHECK is expanded from... aclocal.m4:654: BASH_FUNC_STD_UNSETENV is expanded from... -configure.in:946: the top level]) -m4trace:configure.in:946: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STD_UNSETENV]) -m4trace:configure.in:946: -1- m4_pattern_allow([^HAVE_STD_UNSETENV$]) +configure.in:948: the top level]) m4trace:configure.in:948: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STD_UNSETENV]) m4trace:configure.in:948: -1- m4_pattern_allow([^HAVE_STD_UNSETENV$]) -m4trace:configure.in:951: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +m4trace:configure.in:950: -1- AC_DEFINE_TRACE_LITERAL([HAVE_STD_UNSETENV]) +m4trace:configure.in:950: -1- m4_pattern_allow([^HAVE_STD_UNSETENV$]) +m4trace:configure.in:953: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:878: BASH_FUNC_PRINTF_A_FORMAT is expanded from... -configure.in:951: the top level]) -m4trace:configure.in:951: -1- AC_DEFINE_TRACE_LITERAL([HAVE_PRINTF_A_FORMAT]) -m4trace:configure.in:951: -1- m4_pattern_allow([^HAVE_PRINTF_A_FORMAT$]) -m4trace:configure.in:954: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:953: the top level]) +m4trace:configure.in:953: -1- AC_DEFINE_TRACE_LITERAL([HAVE_PRINTF_A_FORMAT]) +m4trace:configure.in:953: -1- m4_pattern_allow([^HAVE_PRINTF_A_FORMAT$]) +m4trace:configure.in:956: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1297: BASH_SYS_REINSTALL_SIGHANDLERS is expanded from... -configure.in:954: the top level]) -m4trace:configure.in:954: -1- AC_DEFINE_TRACE_LITERAL([MUST_REINSTALL_SIGHANDLERS]) -m4trace:configure.in:954: -1- m4_pattern_allow([^MUST_REINSTALL_SIGHANDLERS$]) -m4trace:configure.in:955: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:956: the top level]) +m4trace:configure.in:956: -1- AC_DEFINE_TRACE_LITERAL([MUST_REINSTALL_SIGHANDLERS]) +m4trace:configure.in:956: -1- m4_pattern_allow([^MUST_REINSTALL_SIGHANDLERS$]) +m4trace:configure.in:957: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1356: BASH_SYS_JOB_CONTROL_MISSING is expanded from... -configure.in:955: the top level]) -m4trace:configure.in:955: -1- AC_DEFINE_TRACE_LITERAL([JOB_CONTROL_MISSING]) -m4trace:configure.in:955: -1- m4_pattern_allow([^JOB_CONTROL_MISSING$]) -m4trace:configure.in:956: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:957: the top level]) +m4trace:configure.in:957: -1- AC_DEFINE_TRACE_LITERAL([JOB_CONTROL_MISSING]) +m4trace:configure.in:957: -1- m4_pattern_allow([^JOB_CONTROL_MISSING$]) +m4trace:configure.in:958: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1415: BASH_SYS_NAMED_PIPES is expanded from... -configure.in:956: the top level]) -m4trace:configure.in:956: -1- AC_DEFINE_TRACE_LITERAL([NAMED_PIPES_MISSING]) -m4trace:configure.in:956: -1- m4_pattern_allow([^NAMED_PIPES_MISSING$]) -m4trace:configure.in:959: -1- AC_DEFINE_TRACE_LITERAL([GWINSZ_IN_SYS_IOCTL]) -m4trace:configure.in:959: -1- m4_pattern_allow([^GWINSZ_IN_SYS_IOCTL$]) -m4trace:configure.in:959: -1- AH_OUTPUT([GWINSZ_IN_SYS_IOCTL], [/* Define to 1 if `TIOCGWINSZ\' requires <sys/ioctl.h>. */ +configure.in:958: the top level]) +m4trace:configure.in:958: -1- AC_DEFINE_TRACE_LITERAL([NAMED_PIPES_MISSING]) +m4trace:configure.in:958: -1- m4_pattern_allow([^NAMED_PIPES_MISSING$]) +m4trace:configure.in:961: -1- AC_DEFINE_TRACE_LITERAL([GWINSZ_IN_SYS_IOCTL]) +m4trace:configure.in:961: -1- m4_pattern_allow([^GWINSZ_IN_SYS_IOCTL$]) +m4trace:configure.in:961: -1- AH_OUTPUT([GWINSZ_IN_SYS_IOCTL], [/* Define to 1 if `TIOCGWINSZ\' requires <sys/ioctl.h>. */ #undef GWINSZ_IN_SYS_IOCTL]) -m4trace:configure.in:960: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +m4trace:configure.in:962: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1496: BASH_HAVE_TIOCSTAT is expanded from... -configure.in:960: the top level]) -m4trace:configure.in:960: -1- AC_DEFINE_TRACE_LITERAL([TIOCSTAT_IN_SYS_IOCTL]) -m4trace:configure.in:960: -1- m4_pattern_allow([^TIOCSTAT_IN_SYS_IOCTL$]) -m4trace:configure.in:961: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +configure.in:962: the top level]) +m4trace:configure.in:962: -1- AC_DEFINE_TRACE_LITERAL([TIOCSTAT_IN_SYS_IOCTL]) +m4trace:configure.in:962: -1- m4_pattern_allow([^TIOCSTAT_IN_SYS_IOCTL$]) +m4trace:configure.in:963: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1508: BASH_HAVE_FIONREAD is expanded from... -configure.in:961: the top level]) -m4trace:configure.in:961: -1- AC_DEFINE_TRACE_LITERAL([FIONREAD_IN_SYS_IOCTL]) -m4trace:configure.in:961: -1- m4_pattern_allow([^FIONREAD_IN_SYS_IOCTL$]) -m4trace:configure.in:963: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:963: the top level]) +m4trace:configure.in:963: -1- AC_DEFINE_TRACE_LITERAL([FIONREAD_IN_SYS_IOCTL]) +m4trace:configure.in:963: -1- m4_pattern_allow([^FIONREAD_IN_SYS_IOCTL$]) +m4trace:configure.in:965: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1938: BASH_CHECK_WCONTINUED is expanded from... -configure.in:963: the top level]) -m4trace:configure.in:963: -1- AC_DEFINE_TRACE_LITERAL([WCONTINUED_BROKEN]) -m4trace:configure.in:963: -1- m4_pattern_allow([^WCONTINUED_BROKEN$]) -m4trace:configure.in:966: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +configure.in:965: the top level]) +m4trace:configure.in:965: -1- AC_DEFINE_TRACE_LITERAL([WCONTINUED_BROKEN]) +m4trace:configure.in:965: -1- m4_pattern_allow([^WCONTINUED_BROKEN$]) +m4trace:configure.in:968: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1526: BASH_CHECK_SPEED_T is expanded from... -configure.in:966: the top level]) -m4trace:configure.in:966: -1- AC_DEFINE_TRACE_LITERAL([SPEED_T_IN_SYS_TYPES]) -m4trace:configure.in:966: -1- m4_pattern_allow([^SPEED_T_IN_SYS_TYPES$]) -m4trace:configure.in:967: -1- AC_DEFINE_TRACE_LITERAL([HAVE_GETPW_DECLS]) -m4trace:configure.in:967: -1- m4_pattern_allow([^HAVE_GETPW_DECLS$]) -m4trace:configure.in:968: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. +configure.in:968: the top level]) +m4trace:configure.in:968: -1- AC_DEFINE_TRACE_LITERAL([SPEED_T_IN_SYS_TYPES]) +m4trace:configure.in:968: -1- m4_pattern_allow([^SPEED_T_IN_SYS_TYPES$]) +m4trace:configure.in:969: -1- AC_DEFINE_TRACE_LITERAL([HAVE_GETPW_DECLS]) +m4trace:configure.in:969: -1- m4_pattern_allow([^HAVE_GETPW_DECLS$]) +m4trace:configure.in:970: -1- _m4_warn([obsolete], [The macro `AC_TRY_RUN' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2592: AC_TRY_RUN is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1653: BASH_CHECK_RTSIGS is expanded from... -configure.in:968: the top level]) -m4trace:configure.in:968: -1- AC_DEFINE_TRACE_LITERAL([UNUSABLE_RT_SIGNALS]) -m4trace:configure.in:968: -1- m4_pattern_allow([^UNUSABLE_RT_SIGNALS$]) -m4trace:configure.in:969: -1- AC_SUBST([SIGLIST_O]) -m4trace:configure.in:969: -1- AC_SUBST_TRACE([SIGLIST_O]) -m4trace:configure.in:969: -1- m4_pattern_allow([^SIGLIST_O$]) -m4trace:configure.in:973: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +configure.in:970: the top level]) +m4trace:configure.in:970: -1- AC_DEFINE_TRACE_LITERAL([UNUSABLE_RT_SIGNALS]) +m4trace:configure.in:970: -1- m4_pattern_allow([^UNUSABLE_RT_SIGNALS$]) +m4trace:configure.in:971: -1- AC_SUBST([SIGLIST_O]) +m4trace:configure.in:971: -1- AC_SUBST_TRACE([SIGLIST_O]) +m4trace:configure.in:971: -1- m4_pattern_allow([^SIGLIST_O$]) +m4trace:configure.in:975: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1605: BASH_CHECK_KERNEL_RLIMIT is expanded from... -configure.in:973: the top level]) -m4trace:configure.in:973: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. +configure.in:975: the top level]) +m4trace:configure.in:975: -1- _m4_warn([obsolete], [The macro `AC_TRY_COMPILE' is obsolete. You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE is expanded from... ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:2462: AC_COMPILE_IFELSE is expanded from... @@ -2447,140 +2451,140 @@ You should run autoupdate.], [../../lib/autoconf/general.m4:2470: AC_TRY_COMPILE ../../lib/m4sugar/m4sh.m4:505: AS_IF is expanded from... ../../lib/autoconf/general.m4:1974: AC_CACHE_VAL is expanded from... aclocal.m4:1605: BASH_CHECK_KERNEL_RLIMIT is expanded from... -configure.in:973: the top level]) -m4trace:configure.in:973: -1- AC_DEFINE_TRACE_LITERAL([RLIMIT_NEEDS_KERNEL]) -m4trace:configure.in:973: -1- m4_pattern_allow([^RLIMIT_NEEDS_KERNEL$]) -m4trace:configure.in:983: -1- AC_SUBST([TERMCAP_LIB]) -m4trace:configure.in:983: -1- AC_SUBST_TRACE([TERMCAP_LIB]) -m4trace:configure.in:983: -1- m4_pattern_allow([^TERMCAP_LIB$]) -m4trace:configure.in:984: -1- AC_SUBST([TERMCAP_DEP]) -m4trace:configure.in:984: -1- AC_SUBST_TRACE([TERMCAP_DEP]) -m4trace:configure.in:984: -1- m4_pattern_allow([^TERMCAP_DEP$]) -m4trace:configure.in:986: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DEV_FD]) -m4trace:configure.in:986: -1- m4_pattern_allow([^HAVE_DEV_FD$]) -m4trace:configure.in:986: -1- AC_DEFINE_TRACE_LITERAL([DEV_FD_PREFIX]) -m4trace:configure.in:986: -1- m4_pattern_allow([^DEV_FD_PREFIX$]) -m4trace:configure.in:986: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DEV_FD]) -m4trace:configure.in:986: -1- m4_pattern_allow([^HAVE_DEV_FD$]) -m4trace:configure.in:986: -1- AC_DEFINE_TRACE_LITERAL([DEV_FD_PREFIX]) -m4trace:configure.in:986: -1- m4_pattern_allow([^DEV_FD_PREFIX$]) -m4trace:configure.in:987: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DEV_STDIN]) -m4trace:configure.in:987: -1- m4_pattern_allow([^HAVE_DEV_STDIN$]) -m4trace:configure.in:988: -1- AC_DEFINE_TRACE_LITERAL([DEFAULT_MAIL_DIRECTORY]) -m4trace:configure.in:988: -1- m4_pattern_allow([^DEFAULT_MAIL_DIRECTORY$]) -m4trace:configure.in:995: -1- AC_DEFINE_TRACE_LITERAL([JOB_CONTROL]) -m4trace:configure.in:995: -1- m4_pattern_allow([^JOB_CONTROL$]) -m4trace:configure.in:1001: -1- AC_SUBST([JOBS_O]) -m4trace:configure.in:1001: -1- AC_SUBST_TRACE([JOBS_O]) -m4trace:configure.in:1001: -1- m4_pattern_allow([^JOBS_O$]) -m4trace:configure.in:1014: -1- AC_DEFINE_TRACE_LITERAL([SVR4_2]) -m4trace:configure.in:1014: -1- m4_pattern_allow([^SVR4_2$]) -m4trace:configure.in:1015: -1- AC_DEFINE_TRACE_LITERAL([SVR4]) -m4trace:configure.in:1015: -1- m4_pattern_allow([^SVR4$]) -m4trace:configure.in:1016: -1- AC_DEFINE_TRACE_LITERAL([SVR4]) -m4trace:configure.in:1016: -1- m4_pattern_allow([^SVR4$]) -m4trace:configure.in:1017: -1- AC_DEFINE_TRACE_LITERAL([SVR5]) -m4trace:configure.in:1017: -1- m4_pattern_allow([^SVR5$]) -m4trace:configure.in:1036: -1- AC_DEFINE_TRACE_LITERAL([PGRP_PIPE]) -m4trace:configure.in:1036: -1- m4_pattern_allow([^PGRP_PIPE$]) -m4trace:configure.in:1083: -1- AC_SUBST([SHOBJ_CC]) -m4trace:configure.in:1083: -1- AC_SUBST_TRACE([SHOBJ_CC]) -m4trace:configure.in:1083: -1- m4_pattern_allow([^SHOBJ_CC$]) -m4trace:configure.in:1084: -1- AC_SUBST([SHOBJ_CFLAGS]) -m4trace:configure.in:1084: -1- AC_SUBST_TRACE([SHOBJ_CFLAGS]) -m4trace:configure.in:1084: -1- m4_pattern_allow([^SHOBJ_CFLAGS$]) -m4trace:configure.in:1085: -1- AC_SUBST([SHOBJ_LD]) -m4trace:configure.in:1085: -1- AC_SUBST_TRACE([SHOBJ_LD]) -m4trace:configure.in:1085: -1- m4_pattern_allow([^SHOBJ_LD$]) -m4trace:configure.in:1086: -1- AC_SUBST([SHOBJ_LDFLAGS]) -m4trace:configure.in:1086: -1- AC_SUBST_TRACE([SHOBJ_LDFLAGS]) -m4trace:configure.in:1086: -1- m4_pattern_allow([^SHOBJ_LDFLAGS$]) -m4trace:configure.in:1087: -1- AC_SUBST([SHOBJ_XLDFLAGS]) -m4trace:configure.in:1087: -1- AC_SUBST_TRACE([SHOBJ_XLDFLAGS]) -m4trace:configure.in:1087: -1- m4_pattern_allow([^SHOBJ_XLDFLAGS$]) -m4trace:configure.in:1088: -1- AC_SUBST([SHOBJ_LIBS]) -m4trace:configure.in:1088: -1- AC_SUBST_TRACE([SHOBJ_LIBS]) -m4trace:configure.in:1088: -1- m4_pattern_allow([^SHOBJ_LIBS$]) -m4trace:configure.in:1089: -1- AC_SUBST([SHOBJ_STATUS]) -m4trace:configure.in:1089: -1- AC_SUBST_TRACE([SHOBJ_STATUS]) -m4trace:configure.in:1089: -1- m4_pattern_allow([^SHOBJ_STATUS$]) -m4trace:configure.in:1121: -1- AC_SUBST([PROFILE_FLAGS]) -m4trace:configure.in:1121: -1- AC_SUBST_TRACE([PROFILE_FLAGS]) -m4trace:configure.in:1121: -1- m4_pattern_allow([^PROFILE_FLAGS$]) -m4trace:configure.in:1123: -1- AC_SUBST([incdir]) -m4trace:configure.in:1123: -1- AC_SUBST_TRACE([incdir]) -m4trace:configure.in:1123: -1- m4_pattern_allow([^incdir$]) -m4trace:configure.in:1124: -1- AC_SUBST([BUILD_DIR]) -m4trace:configure.in:1124: -1- AC_SUBST_TRACE([BUILD_DIR]) -m4trace:configure.in:1124: -1- m4_pattern_allow([^BUILD_DIR$]) -m4trace:configure.in:1127: -1- AC_SUBST([datarootdir]) -m4trace:configure.in:1127: -1- AC_SUBST_TRACE([datarootdir]) -m4trace:configure.in:1127: -1- m4_pattern_allow([^datarootdir$]) -m4trace:configure.in:1128: -1- AC_SUBST([localedir]) -m4trace:configure.in:1128: -1- AC_SUBST_TRACE([localedir]) -m4trace:configure.in:1128: -1- m4_pattern_allow([^localedir$]) -m4trace:configure.in:1130: -1- AC_SUBST([YACC]) -m4trace:configure.in:1130: -1- AC_SUBST_TRACE([YACC]) -m4trace:configure.in:1130: -1- m4_pattern_allow([^YACC$]) -m4trace:configure.in:1131: -1- AC_SUBST([AR]) -m4trace:configure.in:1131: -1- AC_SUBST_TRACE([AR]) -m4trace:configure.in:1131: -1- m4_pattern_allow([^AR$]) -m4trace:configure.in:1132: -1- AC_SUBST([ARFLAGS]) -m4trace:configure.in:1132: -1- AC_SUBST_TRACE([ARFLAGS]) -m4trace:configure.in:1132: -1- m4_pattern_allow([^ARFLAGS$]) -m4trace:configure.in:1134: -1- AC_SUBST([BASHVERS]) -m4trace:configure.in:1134: -1- AC_SUBST_TRACE([BASHVERS]) -m4trace:configure.in:1134: -1- m4_pattern_allow([^BASHVERS$]) -m4trace:configure.in:1135: -1- AC_SUBST([RELSTATUS]) -m4trace:configure.in:1135: -1- AC_SUBST_TRACE([RELSTATUS]) -m4trace:configure.in:1135: -1- m4_pattern_allow([^RELSTATUS$]) -m4trace:configure.in:1136: -1- AC_SUBST([DEBUG]) -m4trace:configure.in:1136: -1- AC_SUBST_TRACE([DEBUG]) -m4trace:configure.in:1136: -1- m4_pattern_allow([^DEBUG$]) -m4trace:configure.in:1137: -1- AC_SUBST([MALLOC_DEBUG]) -m4trace:configure.in:1137: -1- AC_SUBST_TRACE([MALLOC_DEBUG]) -m4trace:configure.in:1137: -1- m4_pattern_allow([^MALLOC_DEBUG$]) -m4trace:configure.in:1139: -1- AC_SUBST([host_cpu]) -m4trace:configure.in:1139: -1- AC_SUBST_TRACE([host_cpu]) -m4trace:configure.in:1139: -1- m4_pattern_allow([^host_cpu$]) -m4trace:configure.in:1140: -1- AC_SUBST([host_vendor]) -m4trace:configure.in:1140: -1- AC_SUBST_TRACE([host_vendor]) -m4trace:configure.in:1140: -1- m4_pattern_allow([^host_vendor$]) -m4trace:configure.in:1141: -1- AC_SUBST([host_os]) -m4trace:configure.in:1141: -1- AC_SUBST_TRACE([host_os]) -m4trace:configure.in:1141: -1- m4_pattern_allow([^host_os$]) -m4trace:configure.in:1143: -1- AC_SUBST([LOCAL_LIBS]) -m4trace:configure.in:1143: -1- AC_SUBST_TRACE([LOCAL_LIBS]) -m4trace:configure.in:1143: -1- m4_pattern_allow([^LOCAL_LIBS$]) -m4trace:configure.in:1144: -1- AC_SUBST([LOCAL_CFLAGS]) -m4trace:configure.in:1144: -1- AC_SUBST_TRACE([LOCAL_CFLAGS]) -m4trace:configure.in:1144: -1- m4_pattern_allow([^LOCAL_CFLAGS$]) -m4trace:configure.in:1145: -1- AC_SUBST([LOCAL_LDFLAGS]) -m4trace:configure.in:1145: -1- AC_SUBST_TRACE([LOCAL_LDFLAGS]) -m4trace:configure.in:1145: -1- m4_pattern_allow([^LOCAL_LDFLAGS$]) -m4trace:configure.in:1146: -1- AC_SUBST([LOCAL_DEFS]) -m4trace:configure.in:1146: -1- AC_SUBST_TRACE([LOCAL_DEFS]) -m4trace:configure.in:1146: -1- m4_pattern_allow([^LOCAL_DEFS$]) -m4trace:configure.in:1151: -1- AC_CONFIG_FILES([Makefile builtins/Makefile lib/readline/Makefile lib/glob/Makefile \ +configure.in:975: the top level]) +m4trace:configure.in:975: -1- AC_DEFINE_TRACE_LITERAL([RLIMIT_NEEDS_KERNEL]) +m4trace:configure.in:975: -1- m4_pattern_allow([^RLIMIT_NEEDS_KERNEL$]) +m4trace:configure.in:985: -1- AC_SUBST([TERMCAP_LIB]) +m4trace:configure.in:985: -1- AC_SUBST_TRACE([TERMCAP_LIB]) +m4trace:configure.in:985: -1- m4_pattern_allow([^TERMCAP_LIB$]) +m4trace:configure.in:986: -1- AC_SUBST([TERMCAP_DEP]) +m4trace:configure.in:986: -1- AC_SUBST_TRACE([TERMCAP_DEP]) +m4trace:configure.in:986: -1- m4_pattern_allow([^TERMCAP_DEP$]) +m4trace:configure.in:988: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DEV_FD]) +m4trace:configure.in:988: -1- m4_pattern_allow([^HAVE_DEV_FD$]) +m4trace:configure.in:988: -1- AC_DEFINE_TRACE_LITERAL([DEV_FD_PREFIX]) +m4trace:configure.in:988: -1- m4_pattern_allow([^DEV_FD_PREFIX$]) +m4trace:configure.in:988: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DEV_FD]) +m4trace:configure.in:988: -1- m4_pattern_allow([^HAVE_DEV_FD$]) +m4trace:configure.in:988: -1- AC_DEFINE_TRACE_LITERAL([DEV_FD_PREFIX]) +m4trace:configure.in:988: -1- m4_pattern_allow([^DEV_FD_PREFIX$]) +m4trace:configure.in:989: -1- AC_DEFINE_TRACE_LITERAL([HAVE_DEV_STDIN]) +m4trace:configure.in:989: -1- m4_pattern_allow([^HAVE_DEV_STDIN$]) +m4trace:configure.in:990: -1- AC_DEFINE_TRACE_LITERAL([DEFAULT_MAIL_DIRECTORY]) +m4trace:configure.in:990: -1- m4_pattern_allow([^DEFAULT_MAIL_DIRECTORY$]) +m4trace:configure.in:997: -1- AC_DEFINE_TRACE_LITERAL([JOB_CONTROL]) +m4trace:configure.in:997: -1- m4_pattern_allow([^JOB_CONTROL$]) +m4trace:configure.in:1003: -1- AC_SUBST([JOBS_O]) +m4trace:configure.in:1003: -1- AC_SUBST_TRACE([JOBS_O]) +m4trace:configure.in:1003: -1- m4_pattern_allow([^JOBS_O$]) +m4trace:configure.in:1016: -1- AC_DEFINE_TRACE_LITERAL([SVR4_2]) +m4trace:configure.in:1016: -1- m4_pattern_allow([^SVR4_2$]) +m4trace:configure.in:1017: -1- AC_DEFINE_TRACE_LITERAL([SVR4]) +m4trace:configure.in:1017: -1- m4_pattern_allow([^SVR4$]) +m4trace:configure.in:1018: -1- AC_DEFINE_TRACE_LITERAL([SVR4]) +m4trace:configure.in:1018: -1- m4_pattern_allow([^SVR4$]) +m4trace:configure.in:1019: -1- AC_DEFINE_TRACE_LITERAL([SVR5]) +m4trace:configure.in:1019: -1- m4_pattern_allow([^SVR5$]) +m4trace:configure.in:1038: -1- AC_DEFINE_TRACE_LITERAL([PGRP_PIPE]) +m4trace:configure.in:1038: -1- m4_pattern_allow([^PGRP_PIPE$]) +m4trace:configure.in:1085: -1- AC_SUBST([SHOBJ_CC]) +m4trace:configure.in:1085: -1- AC_SUBST_TRACE([SHOBJ_CC]) +m4trace:configure.in:1085: -1- m4_pattern_allow([^SHOBJ_CC$]) +m4trace:configure.in:1086: -1- AC_SUBST([SHOBJ_CFLAGS]) +m4trace:configure.in:1086: -1- AC_SUBST_TRACE([SHOBJ_CFLAGS]) +m4trace:configure.in:1086: -1- m4_pattern_allow([^SHOBJ_CFLAGS$]) +m4trace:configure.in:1087: -1- AC_SUBST([SHOBJ_LD]) +m4trace:configure.in:1087: -1- AC_SUBST_TRACE([SHOBJ_LD]) +m4trace:configure.in:1087: -1- m4_pattern_allow([^SHOBJ_LD$]) +m4trace:configure.in:1088: -1- AC_SUBST([SHOBJ_LDFLAGS]) +m4trace:configure.in:1088: -1- AC_SUBST_TRACE([SHOBJ_LDFLAGS]) +m4trace:configure.in:1088: -1- m4_pattern_allow([^SHOBJ_LDFLAGS$]) +m4trace:configure.in:1089: -1- AC_SUBST([SHOBJ_XLDFLAGS]) +m4trace:configure.in:1089: -1- AC_SUBST_TRACE([SHOBJ_XLDFLAGS]) +m4trace:configure.in:1089: -1- m4_pattern_allow([^SHOBJ_XLDFLAGS$]) +m4trace:configure.in:1090: -1- AC_SUBST([SHOBJ_LIBS]) +m4trace:configure.in:1090: -1- AC_SUBST_TRACE([SHOBJ_LIBS]) +m4trace:configure.in:1090: -1- m4_pattern_allow([^SHOBJ_LIBS$]) +m4trace:configure.in:1091: -1- AC_SUBST([SHOBJ_STATUS]) +m4trace:configure.in:1091: -1- AC_SUBST_TRACE([SHOBJ_STATUS]) +m4trace:configure.in:1091: -1- m4_pattern_allow([^SHOBJ_STATUS$]) +m4trace:configure.in:1123: -1- AC_SUBST([PROFILE_FLAGS]) +m4trace:configure.in:1123: -1- AC_SUBST_TRACE([PROFILE_FLAGS]) +m4trace:configure.in:1123: -1- m4_pattern_allow([^PROFILE_FLAGS$]) +m4trace:configure.in:1125: -1- AC_SUBST([incdir]) +m4trace:configure.in:1125: -1- AC_SUBST_TRACE([incdir]) +m4trace:configure.in:1125: -1- m4_pattern_allow([^incdir$]) +m4trace:configure.in:1126: -1- AC_SUBST([BUILD_DIR]) +m4trace:configure.in:1126: -1- AC_SUBST_TRACE([BUILD_DIR]) +m4trace:configure.in:1126: -1- m4_pattern_allow([^BUILD_DIR$]) +m4trace:configure.in:1129: -1- AC_SUBST([datarootdir]) +m4trace:configure.in:1129: -1- AC_SUBST_TRACE([datarootdir]) +m4trace:configure.in:1129: -1- m4_pattern_allow([^datarootdir$]) +m4trace:configure.in:1130: -1- AC_SUBST([localedir]) +m4trace:configure.in:1130: -1- AC_SUBST_TRACE([localedir]) +m4trace:configure.in:1130: -1- m4_pattern_allow([^localedir$]) +m4trace:configure.in:1132: -1- AC_SUBST([YACC]) +m4trace:configure.in:1132: -1- AC_SUBST_TRACE([YACC]) +m4trace:configure.in:1132: -1- m4_pattern_allow([^YACC$]) +m4trace:configure.in:1133: -1- AC_SUBST([AR]) +m4trace:configure.in:1133: -1- AC_SUBST_TRACE([AR]) +m4trace:configure.in:1133: -1- m4_pattern_allow([^AR$]) +m4trace:configure.in:1134: -1- AC_SUBST([ARFLAGS]) +m4trace:configure.in:1134: -1- AC_SUBST_TRACE([ARFLAGS]) +m4trace:configure.in:1134: -1- m4_pattern_allow([^ARFLAGS$]) +m4trace:configure.in:1136: -1- AC_SUBST([BASHVERS]) +m4trace:configure.in:1136: -1- AC_SUBST_TRACE([BASHVERS]) +m4trace:configure.in:1136: -1- m4_pattern_allow([^BASHVERS$]) +m4trace:configure.in:1137: -1- AC_SUBST([RELSTATUS]) +m4trace:configure.in:1137: -1- AC_SUBST_TRACE([RELSTATUS]) +m4trace:configure.in:1137: -1- m4_pattern_allow([^RELSTATUS$]) +m4trace:configure.in:1138: -1- AC_SUBST([DEBUG]) +m4trace:configure.in:1138: -1- AC_SUBST_TRACE([DEBUG]) +m4trace:configure.in:1138: -1- m4_pattern_allow([^DEBUG$]) +m4trace:configure.in:1139: -1- AC_SUBST([MALLOC_DEBUG]) +m4trace:configure.in:1139: -1- AC_SUBST_TRACE([MALLOC_DEBUG]) +m4trace:configure.in:1139: -1- m4_pattern_allow([^MALLOC_DEBUG$]) +m4trace:configure.in:1141: -1- AC_SUBST([host_cpu]) +m4trace:configure.in:1141: -1- AC_SUBST_TRACE([host_cpu]) +m4trace:configure.in:1141: -1- m4_pattern_allow([^host_cpu$]) +m4trace:configure.in:1142: -1- AC_SUBST([host_vendor]) +m4trace:configure.in:1142: -1- AC_SUBST_TRACE([host_vendor]) +m4trace:configure.in:1142: -1- m4_pattern_allow([^host_vendor$]) +m4trace:configure.in:1143: -1- AC_SUBST([host_os]) +m4trace:configure.in:1143: -1- AC_SUBST_TRACE([host_os]) +m4trace:configure.in:1143: -1- m4_pattern_allow([^host_os$]) +m4trace:configure.in:1145: -1- AC_SUBST([LOCAL_LIBS]) +m4trace:configure.in:1145: -1- AC_SUBST_TRACE([LOCAL_LIBS]) +m4trace:configure.in:1145: -1- m4_pattern_allow([^LOCAL_LIBS$]) +m4trace:configure.in:1146: -1- AC_SUBST([LOCAL_CFLAGS]) +m4trace:configure.in:1146: -1- AC_SUBST_TRACE([LOCAL_CFLAGS]) +m4trace:configure.in:1146: -1- m4_pattern_allow([^LOCAL_CFLAGS$]) +m4trace:configure.in:1147: -1- AC_SUBST([LOCAL_LDFLAGS]) +m4trace:configure.in:1147: -1- AC_SUBST_TRACE([LOCAL_LDFLAGS]) +m4trace:configure.in:1147: -1- m4_pattern_allow([^LOCAL_LDFLAGS$]) +m4trace:configure.in:1148: -1- AC_SUBST([LOCAL_DEFS]) +m4trace:configure.in:1148: -1- AC_SUBST_TRACE([LOCAL_DEFS]) +m4trace:configure.in:1148: -1- m4_pattern_allow([^LOCAL_DEFS$]) +m4trace:configure.in:1153: -1- AC_CONFIG_FILES([Makefile builtins/Makefile lib/readline/Makefile lib/glob/Makefile \ lib/intl/Makefile \ lib/malloc/Makefile lib/sh/Makefile lib/termcap/Makefile \ lib/tilde/Makefile doc/Makefile support/Makefile po/Makefile.in \ examples/loadables/Makefile examples/loadables/perl/Makefile]) -m4trace:configure.in:1151: -1- _m4_warn([obsolete], [AC_OUTPUT should be used without arguments. +m4trace:configure.in:1153: -1- _m4_warn([obsolete], [AC_OUTPUT should be used without arguments. You should run autoupdate.], []) -m4trace:configure.in:1151: -1- AC_SUBST([LIB@&t@OBJS], [$ac_libobjs]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([LIB@&t@OBJS]) -m4trace:configure.in:1151: -1- m4_pattern_allow([^LIB@&t@OBJS$]) -m4trace:configure.in:1151: -1- AC_SUBST([LTLIBOBJS], [$ac_ltlibobjs]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([LTLIBOBJS]) -m4trace:configure.in:1151: -1- m4_pattern_allow([^LTLIBOBJS$]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([top_builddir]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([top_build_prefix]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([srcdir]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([abs_srcdir]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([top_srcdir]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([abs_top_srcdir]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([builddir]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([abs_builddir]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([abs_top_builddir]) -m4trace:configure.in:1151: -1- AC_SUBST_TRACE([INSTALL]) +m4trace:configure.in:1153: -1- AC_SUBST([LIB@&t@OBJS], [$ac_libobjs]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([LIB@&t@OBJS]) +m4trace:configure.in:1153: -1- m4_pattern_allow([^LIB@&t@OBJS$]) +m4trace:configure.in:1153: -1- AC_SUBST([LTLIBOBJS], [$ac_ltlibobjs]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([LTLIBOBJS]) +m4trace:configure.in:1153: -1- m4_pattern_allow([^LTLIBOBJS$]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([top_builddir]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([top_build_prefix]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([srcdir]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([abs_srcdir]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([top_srcdir]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([abs_top_srcdir]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([builddir]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([abs_builddir]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([abs_top_builddir]) +m4trace:configure.in:1153: -1- AC_SUBST_TRACE([INSTALL]) @@ -359,7 +359,7 @@ save_history () the history file. */ using_history (); - if (history_lines_this_session < where_history () || force_append_history) + if (history_lines_this_session <= where_history () || force_append_history) append_history (history_lines_this_session, hf); else write_history (hf); @@ -376,7 +376,7 @@ maybe_append_history (filename) struct stat buf; result = EXECUTION_SUCCESS; - if (history_lines_this_session && (history_lines_this_session < where_history ())) + if (history_lines_this_session && (history_lines_this_session <= where_history ())) { /* If the filename was supplied, then create it if necessary. */ if (stat (filename, &buf) == -1 && errno == ENOENT) diff --git a/bashhist.c~ b/bashhist.c~ index d39dea27..20afbed9 100644 --- a/bashhist.c~ +++ b/bashhist.c~ @@ -213,6 +213,9 @@ bash_history_inhibit_expansion (string, i) else if (i > 1 && string[i - 1] == '{' && string[i - 2] == '$' && member ('}', string + i + 1)) return (1); + /* The shell uses $! as a defined parameter expansion. */ + else if (i > 1 && string[i - 1] == '$' && string[i] == '!') + return (1); #if defined (EXTENDED_GLOB) else if (extended_glob && i > 1 && string[i+1] == '(' && member (')', string + i + 2)) return (1); @@ -356,7 +359,7 @@ save_history () the history file. */ using_history (); - if (history_lines_this_session < where_history () || force_append_history) + if (history_lines_this_session <= where_history () || force_append_history) append_history (history_lines_this_session, hf); else write_history (hf); @@ -373,7 +376,8 @@ maybe_append_history (filename) struct stat buf; result = EXECUTION_SUCCESS; - if (history_lines_this_session && (history_lines_this_session < where_history ())) + /* XXX - why isn't this <= where_history() ? */ + if (history_lines_this_session && (history_lines_this_session <= where_history ())) { /* If the filename was supplied, then create it if necessary. */ if (stat (filename, &buf) == -1 && errno == ENOENT) @@ -96,6 +96,7 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select, #define W_NOPROCSUB 0x100000 /* don't perform process substitution */ #define W_HASCTLESC 0x200000 /* word contains literal CTLESC characters */ #define W_ASSIGNASSOC 0x400000 /* word looks like associative array assignment */ +#define W_ARRAYIND 0x800000 /* word is an array index being expanded */ /* Possible values for subshell_environment */ #define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */ @@ -169,7 +169,7 @@ typedef struct element { #define CMD_STDIN_REDIR 0x400 /* async command needs implicit </dev/null */ #define CMD_COMMAND_BUILTIN 0x0800 /* command executed by `command' builtin */ #define CMD_COPROC_SUBSHELL 0x1000 -#defien CMD_LASTPIPE 0x2000 +#define CMD_LASTPIPE 0x2000 /* What a command looks like. */ typedef struct command { diff --git a/config.h.in b/config.h.in index 4f9e47ff..6b1fc4af 100644 --- a/config.h.in +++ b/config.h.in @@ -215,6 +215,9 @@ /* The number of bytes in a double (hopefully 8). */ #undef SIZEOF_DOUBLE +/* The number of bytes in an `intmax_t'. */ +#undef SIZEOF_INTMAX_T + /* The number of bytes in a `long long', if we have one. */ #undef SIZEOF_LONG_LONG diff --git a/config.h.in~ b/config.h.in~ index bb11a7c1..4f9e47ff 100644 --- a/config.h.in~ +++ b/config.h.in~ @@ -415,6 +415,8 @@ #undef HAVE_STRUCT_TIMEZONE +#undef WEXITSTATUS_OFFSET + /* Characteristics of definitions in the system header files. */ #undef HAVE_GETPW_DECLS @@ -27176,6 +27176,366 @@ _ACEOF fi +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:$LINENO: checking size of intmax_t" >&5 +$as_echo_n "checking size of intmax_t... " >&6; } +if test "${ac_cv_sizeof_intmax_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long int) (sizeof (intmax_t))) >= 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_lo=0 ac_mid=0 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long int) (sizeof (intmax_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_hi=$ac_mid; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_lo=`expr $ac_mid + 1` + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid + 1` +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long int) (sizeof (intmax_t))) < 0)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_hi=-1 ac_mid=-1 + while :; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long int) (sizeof (intmax_t))) >= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_lo=$ac_mid; break +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_hi=`expr '(' $ac_mid ')' - 1` + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + ac_mid=`expr 2 '*' $ac_mid` +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_lo= ac_hi= +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + ac_mid=`expr '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo` + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +static int test_array [1 - 2 * !(((long int) (sizeof (intmax_t))) <= $ac_mid)]; +test_array [0] = 0 + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_hi=$ac_mid +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_lo=`expr '(' $ac_mid ')' + 1` +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in +?*) ac_cv_sizeof_intmax_t=$ac_lo;; +'') if test "$ac_cv_type_intmax_t" = yes; then + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute sizeof (intmax_t) +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute sizeof (intmax_t) +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } + else + ac_cv_sizeof_intmax_t=0 + fi ;; +esac +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +static long int longval () { return (long int) (sizeof (intmax_t)); } +static unsigned long int ulongval () { return (long int) (sizeof (intmax_t)); } +#include <stdio.h> +#include <stdlib.h> +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (((long int) (sizeof (intmax_t))) < 0) + { + long int i = longval (); + if (i != ((long int) (sizeof (intmax_t)))) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ((long int) (sizeof (intmax_t)))) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_sizeof_intmax_t=`cat conftest.val` +else + $as_echo "$as_me: program exited with status $ac_status" >&5 +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +if test "$ac_cv_type_intmax_t" = yes; then + { { $as_echo "$as_me:$LINENO: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ { $as_echo "$as_me:$LINENO: error: cannot compute sizeof (intmax_t) +See \`config.log' for more details." >&5 +$as_echo "$as_me: error: cannot compute sizeof (intmax_t) +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; }; } + else + ac_cv_sizeof_intmax_t=0 + fi +fi +rm -rf conftest.dSYM +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +rm -f conftest.val +fi +{ $as_echo "$as_me:$LINENO: result: $ac_cv_sizeof_intmax_t" >&5 +$as_echo "$ac_cv_sizeof_intmax_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_INTMAX_T $ac_cv_sizeof_intmax_t +_ACEOF + + + { $as_echo "$as_me:$LINENO: checking for struct termios.c_line" >&5 $as_echo_n "checking for struct termios.c_line... " >&6; } diff --git a/configure.in b/configure.in index 4c9289aa..d9f4fbbb 100644 --- a/configure.in +++ b/configure.in @@ -905,6 +905,8 @@ BASH_CHECK_TYPE(socklen_t, [#include <sys/socket.h>], int, HAVE_SOCKLEN_T) fi BASH_TYPE_RLIMIT +AC_CHECK_SIZEOF(intmax_t, 8) + dnl presence and contents of structures used by system calls BASH_STRUCT_TERMIOS_LDISC BASH_STRUCT_TERMIO_LDISC diff --git a/configure.in~ b/configure.in~ index 0af9555d..4c9289aa 100644 --- a/configure.in~ +++ b/configure.in~ @@ -21,7 +21,7 @@ dnl Process this file with autoconf to produce a configure script. # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -AC_REVISION([for Bash 4.2, version 4.031])dnl +AC_REVISION([for Bash 4.2, version 4.032])dnl define(bashvers, 4.2) define(relstatus, devel) @@ -918,6 +918,8 @@ AC_STRUCT_TM AC_STRUCT_TIMEZONE BASH_STRUCT_TIMEZONE +BASH_STRUCT_WEXITSTATUS_OFFSET + dnl presence and behavior of C library functions BASH_FUNC_STRSIGNAL BASH_FUNC_OPENDIR_CHECK @@ -5,12 +5,12 @@ .\" Case Western Reserve University .\" chet@po.cwru.edu .\" -.\" Last Change: Wed Jul 21 08:47:30 EDT 2010 +.\" Last Change: Tue Aug 3 15:24:33 EDT 2010 .\" .\" bash_builtins, strip all but Built-Ins section .if \n(zZ=1 .ig zZ .if \n(zY=1 .ig zY -.TH BASH 1 "2010 July 21" "GNU Bash-4.2" +.TH BASH 1 "2010 August 3" "GNU Bash-4.2" .\" .\" There's some problem with having a `@' .\" in a tagged paragraph with the BSD man macros. @@ -408,7 +408,7 @@ No other startup files are read. .PP .B Bash attempts to determine when it is being run with its standard input -connected to a a network connection, as if by the remote shell +connected to a network connection, as if by the remote shell daemon, usually \fIrshd\fP, or the secure shell daemon \fIsshd\fP. If .B bash @@ -1883,7 +1883,7 @@ If \fBbash\fP finds this variable in the environment when the shell starts with value .if t \f(CWt\fP, .if n "t", -it assumes that the shell is running in an emacs shell buffer and disables +it assumes that the shell is running in an Emacs shell buffer and disables line editing. .TP .B ENV @@ -2300,8 +2300,8 @@ terminates after waiting for that number of seconds if input does not arrive. .TP .B TMPDIR -If set, \fBBash\fP uses its value as the name of a directory in which -\fBBash\fP creates temporary files for the shell's use. +If set, \fBbash\fP uses its value as the name of a directory in which +\fBbash\fP creates temporary files for the shell's use. .TP .B auto_resume This variable controls how the shell interacts with the user and @@ -6357,6 +6357,8 @@ writing the history file. .PP An event designator is a reference to a command line entry in the history list. +Unless the reference is absolute, events are relative to the current +position in the history list. .PP .PD 0 .TP @@ -6372,25 +6374,27 @@ Refer to command line .IR n . .TP .B !\-\fIn\fR -Refer to the current command line minus +Refer to the current command minus .IR n . .TP .B !! Refer to the previous command. This is a synonym for `!\-1'. .TP .B !\fIstring\fR -Refer to the most recent command starting with +Refer to the most recent command preceding the current position in the +history list starting with .IR string . .TP .B !?\fIstring\fR\fB[?]\fR -Refer to the most recent command containing +Refer to the most recent command preceding the current postition in the +history list containing .IR string . The trailing \fB?\fP may be omitted if .I string is followed immediately by a newline. .TP .B \d\s+2^\s-2\u\fIstring1\fP\d\s+2^\s-2\u\fIstring2\fP\d\s+2^\s-2\u -Quick substitution. Repeat the last command, replacing +Quick substitution. Repeat the previous command, replacing .I string1 with .IR string2 . diff --git a/doc/bash.1~ b/doc/bash.1~ index 626aed70..33b0d309 100644 --- a/doc/bash.1~ +++ b/doc/bash.1~ @@ -5,12 +5,12 @@ .\" Case Western Reserve University .\" chet@po.cwru.edu .\" -.\" Last Change: Fri Jul 2 17:31:49 EDT 2010 +.\" Last Change: Tue Aug 3 15:24:33 EDT 2010 .\" .\" bash_builtins, strip all but Built-Ins section .if \n(zZ=1 .ig zZ .if \n(zY=1 .ig zY -.TH BASH 1 "2010 July 2" "GNU Bash-4.2" +.TH BASH 1 "2010 August 3" "GNU Bash-4.2" .\" .\" There's some problem with having a `@' .\" in a tagged paragraph with the BSD man macros. @@ -408,7 +408,7 @@ No other startup files are read. .PP .B Bash attempts to determine when it is being run with its standard input -connected to a a network connection, as if by the remote shell +connected to a network connection, as if by the remote shell daemon, usually \fIrshd\fP, or the secure shell daemon \fIsshd\fP. If .B bash @@ -950,7 +950,11 @@ A shell function is an object that is called like a simple command and executes a compound command with a new set of positional parameters. Shell functions are declared as follows: .TP -[ \fBfunction\fP ] \fIname\fP () \fIcompound\-command\fP [\fIredirection\fP] +\fIname\fP () \fIcompound\-command\fP [\fIredirection\fP] +.PD 0 +.TP +\fBfunction\fP \fIname\fP [()] \fIcompound\-command\fP [\fIredirection\fP] +.PD This defines a function named \fIname\fP. The reserved word \fBfunction\fP is optional. If the \fBfunction\fP reserved word is supplied, the parentheses are optional. @@ -1879,7 +1883,7 @@ If \fBbash\fP finds this variable in the environment when the shell starts with value .if t \f(CWt\fP, .if n "t", -it assumes that the shell is running in an emacs shell buffer and disables +it assumes that the shell is running in an Emacs shell buffer and disables line editing. .TP .B ENV @@ -2102,12 +2106,13 @@ for printing selection lists. Automatically set upon receipt of a .BR SIGWINCH . .TP .B MAIL -If this parameter is set to a file name and the +If this parameter is set to a file or directory name and the .SM .B MAILPATH variable is not set, .B bash -informs the user of the arrival of mail in the specified file. +informs the user of the arrival of mail in the specified file or +Maildir-format directory. .TP .B MAILCHECK Specifies how @@ -2295,8 +2300,8 @@ terminates after waiting for that number of seconds if input does not arrive. .TP .B TMPDIR -If set, \fBBash\fP uses its value as the name of a directory in which -\fBBash\fP creates temporary files for the shell's use. +If set, \fBbash\fP uses its value as the name of a directory in which +\fBbash\fP creates temporary files for the shell's use. .TP .B auto_resume This variable controls how the shell interacts with the user and @@ -6352,6 +6357,8 @@ writing the history file. .PP An event designator is a reference to a command line entry in the history list. +Unless the reference is absolute, events are relative to the current +position in the history list. .PP .PD 0 .TP @@ -6367,25 +6374,27 @@ Refer to command line .IR n . .TP .B !\-\fIn\fR -Refer to the current command line minus +Refer to the current command minus .IR n . .TP .B !! Refer to the previous command. This is a synonym for `!\-1'. .TP .B !\fIstring\fR -Refer to the most recent command starting with +Refer to the most recent command preceding the current position in the +history list starting with .IR string . .TP .B !?\fIstring\fR\fB[?]\fR -Refer to the most recent command containing +Refer to the most recent command preceding the current postition in the +history list containing .IR string . The trailing \fB?\fP may be omitted if .I string is followed immediately by a newline. .TP .B \d\s+2^\s-2\u\fIstring1\fP\d\s+2^\s-2\u\fIstring2\fP\d\s+2^\s-2\u -Quick substitution. Repeat the last command, replacing +Quick substitution. Repeat the previous command, replacing .I string1 with .IR string2 . @@ -7338,6 +7347,8 @@ backspace suppress further output .TP .B \ee +.TP +.B \eE an escape character .TP .B \ef diff --git a/doc/bashref.texi b/doc/bashref.texi index db249617..1a94dc03 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -5058,7 +5058,7 @@ it is subsequently reset. @item EMACS If Bash finds this variable in the environment when the shell starts with value @samp{t}, it assumes that the shell is running in an -emacs shell buffer and disables line editing. +Emacs shell buffer and disables line editing. @item ENV Similar to @code{BASH_ENV}; used when the shell is invoked in @@ -5728,7 +5728,7 @@ No other startup files are read. @subsubheading Invoked by remote shell daemon Bash attempts to determine when it is being run with its standard input -connected to a a network connection, as if by the remote shell +connected to a network connection, as if by the remote shell daemon, usually @code{rshd}, or the secure shell daemon @code{sshd}. If Bash determines it is being run in this fashion, it reads and executes commands from @file{~/.bashrc}, if that diff --git a/doc/bashref.texi~ b/doc/bashref.texi~ index 1f3bacbe..db249617 100644 --- a/doc/bashref.texi~ +++ b/doc/bashref.texi~ @@ -820,7 +820,7 @@ until it evaluates to zero. Each time @var{expr2} evaluates to a non-zero value, @var{commands} are executed and the arithmetic expression @var{expr3} is evaluated. If any expression is omitted, it behaves as if it evaluates to 1. -The return value is the exit status of the last command in @var{list} +The return value is the exit status of the last command in @var{commands} that is executed, or false if any of the expressions is invalid. @end table @@ -1233,7 +1233,8 @@ shell context; no new process is created to interpret them. Functions are declared using this syntax: @rwindex function @example -[ @code{function} ] @var{name} () @var{compound-command} [ @var{redirections} ] +@var{name} () @var{compound-command} [ @var{redirections} ]@*or@* +@code{function} @var{name} [()] @var{compound-command} [ @var{redirections} ] @end example This defines a shell function named @var{name}. The reserved @@ -3650,6 +3651,7 @@ backspace @item \c suppress further output @item \e +@itemx \E escape @item \f form feed @@ -4783,9 +4785,10 @@ A list of characters that separate fields; used when the shell splits words as part of expansion. @item MAIL -If this parameter is set to a filename and the @env{MAILPATH} variable +If this parameter is set to a filename or directory name +and the @env{MAILPATH} variable is not set, Bash informs the user of the arrival of mail in -the specified file. +the specified file or Maildir-format directory. @item MAILPATH A colon-separated list of filenames which the shell periodically checks diff --git a/execute_cmd.c~ b/execute_cmd.c~ index 7c3526c1..e5e2a8eb 100644 --- a/execute_cmd.c~ +++ b/execute_cmd.c~ @@ -2212,17 +2212,13 @@ execute_pipeline (command, asynchronous, pipe_in, pipe_out, fds_to_close) lastpipe_flag = 1; freeze_jobs_list (); lastpipe_jid = stop_pipeline (0, (COMMAND *)NULL); /* XXX */ - add_unwind_protect (lastpipe_cleanup, lastpipe_flag); + add_unwind_protect (lastpipe_cleanup, lastpipe_jid); } cmd->flags |= CMD_LASTPIPE; } if (prev >= 0) add_unwind_protect (close, prev); - /* XXX - might need to temporarily put shell process in pgrp of the pipeline, - so after we give the terminal to that process group in stop_pipeline, the - shell can still access it. Would need to give it to - jobs[lastpipe_jid]->pgrp */ exec_result = execute_command_internal (cmd, asynchronous, prev, pipe_out, fds_to_close); if (lstdin > 0) @@ -4333,6 +4329,7 @@ execute_shell_function (var, words) int ret; struct fd_bitmap *bitmap; +itrace("execute_shell_function: %s", var->name); bitmap = new_fd_bitmap (FD_BITMAP_DEFAULT_SIZE); begin_unwind_frame ("execute-shell-function"); add_unwind_protect (dispose_fd_bitmap, (char *)bitmap); @@ -1,6 +1,6 @@ /* expr.c -- arithmetic expression evaluation. */ -/* Copyright (C) 1990-2009 Free Software Foundation, Inc. +/* Copyright (C) 1990-2010 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -309,6 +309,7 @@ static void expr_bind_variable (lhs, rhs) char *lhs, *rhs; { +itrace("expr_bind_variable: %s = %s", lhs, rhs); (void)bind_int_variable (lhs, rhs); stupidly_hack_special_variables (lhs); } @@ -1000,6 +1001,7 @@ expr_streval (tok, e, lvalue) arrayind_t ind; #endif +itrace("expr_streval: %s", tok); /* [[[[[ */ #if defined (ARRAY_VARS) v = (e == ']') ? array_variable_part (tok, (char **)0, (int *)0) : find_variable (tok); @@ -482,4 +482,13 @@ extern void zsyncfd __P((int)); /* declarations for functions defined in lib/sh/zwrite.c */ extern int zwrite __P((int, char *, size_t)); +/* declarations for functions defined in lib/glob/gmisc.c */ +extern int match_pattern_char __P((char *, char *)); +extern int umatchlen __P((char *, size_t)); + +#if defined (HANDLE_MULTIBYTE) +extern int match_pattern_wchar __P((wchar_t *, wchar_t *)); +extern int wmatchlen __P((wchar_t *, size_t)); +#endif + #endif /* _EXTERNS_H_ */ @@ -1,7 +1,7 @@ /* externs.h -- extern function declarations which do not appear in their own header file. */ -/* Copyright (C) 1993-2009 Free Software Foundation, Inc. +/* Copyright (C) 1993-2010 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -482,4 +482,13 @@ extern void zsyncfd __P((int)); /* declarations for functions defined in lib/sh/zwrite.c */ extern int zwrite __P((int, char *, size_t)); +/* declarations for functions defined in lib/glob/gmisc.c */ +extern int match_pattern_char __P((char *, char *)); +extern size_t umatchlen __P((char *, size_t)); + +#if defined (HANDLE_MULTIBYTE) +extern int match_pattern_wchar __P((wchar_t *, wchar_t *)); +extern size_t wmatchlen __P((wchar_t *, size_t)); +#endif + #endif /* _EXTERNS_H_ */ diff --git a/include/typemax.h b/include/typemax.h index 32e7a899..d7b32d93 100644 --- a/include/typemax.h +++ b/include/typemax.h @@ -77,6 +77,17 @@ static const unsigned long long int maxquad = ULLONG_MAX; # define ULLONG_MAX maxquad #endif +#if SIZEOF_INTMAX_T == SIZEOF_LONG_LONG +# define INTMAX_MAX LLONG_MAX +# define INTMAX_MIN LLONG_MIN +#elif SIZEOF_INTMAX_T == SIZEOF_LONG +# define INTMAX_MAX LONG_MAX +# define INTMAX_MIN LONG_MIN +#else +# define INTMAX_MAX INT_MAX +# define INTMAX_MIN INT_MIN +#endif + #ifndef SSIZE_MAX # define SSIZE_MAX 32767 /* POSIX minimum max */ #endif diff --git a/include/typemax.h~ b/include/typemax.h~ new file mode 100644 index 00000000..32e7a899 --- /dev/null +++ b/include/typemax.h~ @@ -0,0 +1,84 @@ +/* typemax.h -- encapsulate max values for long, long long, etc. */ + +/* Copyright (C) 2001 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + * NOTE: This should be included after config.h, limits.h, stdint.h, and + * inttypes.h + */ + +#ifndef _SH_TYPEMAX_H +#define _SH_TYPEMAX_H + +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + +/* Nonzero if the integer type T is signed. */ +#ifndef TYPE_SIGNED +# define TYPE_SIGNED(t) (! ((t) 0 < (t) -1)) +#endif + +#ifndef TYPE_MINIMUM +# define TYPE_MINIMUM(t) ((t) (TYPE_SIGNED (t) \ + ? ~ (t) 0 << (sizeof (t) * CHAR_BIT - 1) \ + : (t) 0)) +#endif + +#ifndef TYPE_MAXIMUM +# define TYPE_MAXIMUM(t) ((t) (~ (t) 0 - TYPE_MINIMUM (t))) +#endif + +#ifdef HAVE_LONG_LONG +# ifndef LLONG_MAX +# define LLONG_MAX TYPE_MAXIMUM(long long int) +# define LLONG_MIN TYPE_MINIMUM(long long int) +# endif +# ifndef ULLONG_MAX +# define ULLONG_MAX TYPE_MAXIMUM(unsigned long long int) +# endif +#endif + +#ifndef ULONG_MAX +# define ULONG_MAX ((unsigned long) ~(unsigned long) 0) +#endif + +#ifndef LONG_MAX +# define LONG_MAX ((long int) (ULONG_MAX >> 1)) +# define LONG_MIN ((long int) (-LONG_MAX - 1L)) +#endif + +#ifndef INT_MAX /* ouch */ +# define INT_MAX TYPE_MAXIMUM(int) +# define INT_MIN TYPE_MINIMUM(int) +# define UINT_MAX ((unsigned int) ~(unsigned int)0) +#endif + +/* workaround for gcc bug in versions < 2.7 */ +#if defined (HAVE_LONG_LONG) && __GNUC__ == 2 && __GNUC_MINOR__ < 7 +static const unsigned long long int maxquad = ULLONG_MAX; +# undef ULLONG_MAX +# define ULLONG_MAX maxquad +#endif + +#ifndef SSIZE_MAX +# define SSIZE_MAX 32767 /* POSIX minimum max */ +#endif + +#endif /* _SH_TYPEMAX_H */ diff --git a/lib/glob/Makefile.in b/lib/glob/Makefile.in index 000c231a..12cbb61c 100644 --- a/lib/glob/Makefile.in +++ b/lib/glob/Makefile.in @@ -71,7 +71,7 @@ CSOURCES = $(srcdir)/glob.c $(srcdir)/strmatch.c $(srcdir)/smatch.c \ # The header files for this library. HSOURCES = $(srcdir)/strmatch.h -OBJECTS = glob.o strmatch.o smatch.o xmbsrtowcs.o +OBJECTS = glob.o strmatch.o smatch.o xmbsrtowcs.o gmisc.o # The texinfo files which document this library. DOCSOURCE = doc/glob.texi @@ -147,12 +147,17 @@ glob.o: strmatch.h glob.h glob.o: $(BASHINCDIR)/shmbutil.h glob.o: $(topdir)/xmalloc.h +gmisc.o: $(BUILD_DIR)/config.h +gmisc.o: $(topdir)/bashtypes.h $(BASHINCDIR)/ansi_stdlib.h $(topdir)/bashansi.h +gmisc.o: $(BASHINCDIR)/shmbutil.h + xmbsrtowcs.o: ${BUILD_DIR}/config.h xmbsrtowcs.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h xmbsrtowcs.o: ${BASHINCDIR}/shmbutil.h # Rules for deficient makes, like SunOS and Solaris glob.o: glob.c +gmisc.o: gmisc.c strmatch.o: strmatch.c smatch.o: smatch.c xmbsrtowcs.o: xmbsrtowcs.c diff --git a/lib/glob/Makefile.in~ b/lib/glob/Makefile.in~ new file mode 100644 index 00000000..000c231a --- /dev/null +++ b/lib/glob/Makefile.in~ @@ -0,0 +1,162 @@ +## -*- text -*- #################################################### +# # +# Makefile for the GNU Glob Library. # +# # +#################################################################### +# +# Copyright (C) 1996-2009 Free Software Foundation, Inc. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +srcdir = @srcdir@ +VPATH = .:@srcdir@ +topdir = @top_srcdir@ +BUILD_DIR = @BUILD_DIR@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +CC = @CC@ +RANLIB = @RANLIB@ +AR = @AR@ +ARFLAGS = @ARFLAGS@ +RM = rm -f +CP = cp +MV = mv + +SHELL = @MAKE_SHELL@ + +PROFILE_FLAGS = @PROFILE_FLAGS@ + +CFLAGS = @CFLAGS@ +LOCAL_CFLAGS = @LOCAL_CFLAGS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ @LOCAL_LDFLAGS@ + +DEFS = @DEFS@ +LOCAL_DEFS = @LOCAL_DEFS@ + +BASHINCDIR = ${topdir}/include + +INCLUDES = -I. -I../.. -I$(topdir) -I$(BASHINCDIR) -I$(topdir)/lib + +CCFLAGS = $(PROFILE_FLAGS) $(DEFS) $(LOCAL_DEFS) $(CPPFLAGS) ${INCLUDES} \ + $(LOCAL_CFLAGS) $(CFLAGS) + +# Here is a rule for making .o files from .c files that doesn't force +# the type of the machine (like -sun3) into the flags. +.c.o: + $(RM) $@ + $(CC) -c $(CCFLAGS) $< + +# The name of the library target. +LIBRARY_NAME = libglob.a + +# The C code source files for this library. +CSOURCES = $(srcdir)/glob.c $(srcdir)/strmatch.c $(srcdir)/smatch.c \ + $(srcdir)/xmbsrtowcs.c + +# The header files for this library. +HSOURCES = $(srcdir)/strmatch.h + +OBJECTS = glob.o strmatch.o smatch.o xmbsrtowcs.o + +# The texinfo files which document this library. +DOCSOURCE = doc/glob.texi +DOCOBJECT = doc/glob.dvi +DOCSUPPORT = doc/Makefile +DOCUMENTATION = $(DOCSOURCE) $(DOCOBJECT) $(DOCSUPPORT) + +SUPPORT = Makefile ChangeLog $(DOCSUPPORT) + +SOURCES = $(CSOURCES) $(HSOURCES) $(DOCSOURCE) + +THINGS_TO_TAR = $(SOURCES) $(SUPPORT) + +###################################################################### + +all: $(LIBRARY_NAME) + +$(LIBRARY_NAME): $(OBJECTS) + $(RM) -f $@ + $(AR) $(ARFLAGS) $@ $(OBJECTS) + -test -n "$(RANLIB)" && $(RANLIB) $@ + +what-tar: + @for file in $(THINGS_TO_TAR); do \ + echo $(selfdir)$$file; \ + done + +documentation: force + -(cd doc; $(MAKE) $(MFLAGS)) +force: + +# The rule for 'includes' is written funny so that the if statement +# always returns TRUE unless there really was an error installing the +# include files. +install: + +clean: + rm -f $(OBJECTS) $(LIBRARY_NAME) + -(cd doc && $(MAKE) $(MFLAGS) $@ ) + +realclean distclean maintainer-clean: clean + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + $(RM) -f Makefile + +mostlyclean: clean + -( cd doc && $(MAKE) $(MFLAGS) $@ ) + +${BUILD_DIR}/pathnames.h: ${BUILD_DIR}/config.h ${BUILD_DIR}/Makefile Makefile + -( cd ${BUILD_DIR} && ${MAKE} ${MFLAGS} pathnames.h ) + +###################################################################### +# # +# Dependencies for the object files which make up this library. # +# # +###################################################################### + +smatch.o: strmatch.h +smatch.o: $(BUILD_DIR)/config.h +smatch.o: $(BASHINCDIR)/chartypes.h +smatch.o: $(BASHINCDIR)/ansi_stdlib.h $(topdir)/bashansi.h +smatch.o: $(BASHINCDIR)/shmbutil.h +smatch.o: $(topdir)/xmalloc.h + +strmatch.o: strmatch.h +strmatch.o: $(BUILD_DIR)/config.h +strmatch.o: $(BASHINCDIR)/stdc.h + +glob.o: $(BUILD_DIR)/config.h +glob.o: $(topdir)/shell.h $(BUILD_DIR)/pathnames.h +glob.o: $(topdir)/bashtypes.h $(BASHINCDIR)/ansi_stdlib.h $(topdir)/bashansi.h +glob.o: $(BASHINCDIR)/posixstat.h $(BASHINCDIR)/memalloc.h +glob.o: strmatch.h glob.h +glob.o: $(BASHINCDIR)/shmbutil.h +glob.o: $(topdir)/xmalloc.h + +xmbsrtowcs.o: ${BUILD_DIR}/config.h +xmbsrtowcs.o: ${topdir}/bashansi.h ${BASHINCDIR}/ansi_stdlib.h +xmbsrtowcs.o: ${BASHINCDIR}/shmbutil.h + +# Rules for deficient makes, like SunOS and Solaris +glob.o: glob.c +strmatch.o: strmatch.c +smatch.o: smatch.c +xmbsrtowcs.o: xmbsrtowcs.c + +# dependencies for C files that include other C files +glob.o: glob_loop.c +smatch.o: sm_loop.c diff --git a/lib/glob/gmisc.c b/lib/glob/gmisc.c new file mode 100644 index 00000000..2499ba1d --- /dev/null +++ b/lib/glob/gmisc.c @@ -0,0 +1,311 @@ +/* gmisc.c -- miscellaneous pattern matching utility functions for Bash. + + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne-Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" +#include "shmbutil.h" + +#include "stdc.h" + +#ifndef RPAREN +# define RPAREN '(' +#endif +#ifndef LPAREN +# define LPAREN ')' +#endif + +#if defined (HANDLE_MULTIBYTE) +/* Return 1 of the first character of WSTRING could match the first + character of pattern WPAT. Wide character version. */ +int +match_pattern_wchar (wpat, wstring) + wchar_t *wpat, *wstring; +{ + wchar_t wc; + + if (*wstring == 0) + return (0); + + switch (wc = *wpat++) + { + default: + return (*wstring == wc); + case L'\\': + return (*wstring == *wpat); + case L'?': + return (*wpat == LPAREN ? 1 : (*wstring != L'\0')); + case L'*': + return (1); + case L'+': + case L'!': + case L'@': + return (*wpat == LPAREN ? 1 : (*wstring == wc)); + case L'[': + return (*wstring != L'\0'); + } +} + +int +wmatchlen (wpat, wmax) + wchar_t *wpat; + size_t wmax; +{ + wchar_t wc, *wbrack; + int matlen, t, in_cclass, in_collsym, in_equiv; + + if (*wpat == 0) + return (0); + + matlen = 0; + while (wc = *wpat++) + { + switch (wc) + { + default: + matlen++; + break; + case L'\\': + if (*wpat == 0) + return ++matlen; + else + { + matlen++; + wpat++; + } + break; + case L'?': + if (*wpat == LPAREN) + return (matlen = -1); /* XXX for now */ + else + matlen++; + break; + case L'*': + return (matlen = -1); + case L'+': + case L'!': + case L'@': + if (*wpat == LPAREN) + return (matlen = -1); /* XXX for now */ + else + matlen++; + break; + case L'[': + /* scan for ending `]', skipping over embedded [:...:] */ + wbrack = wpat; + wc = *wpat++; + do + { + if (wc == 0) + { + matlen += wpat - wbrack - 1; /* incremented below */ + break; + } + else if (wc == L'\\') + { + wc = *wpat++; + if (*wpat == 0) + break; + } + else if (wc == L'[' && *wpat == L':') /* character class */ + { + wpat++; + in_cclass = 1; + } + else if (in_cclass && wc == L':' && *wpat == L']') + { + wpat++; + in_cclass = 0; + } + else if (wc == L'[' && *wpat == L'.') /* collating symbol */ + { + wpat++; + if (*wpat == L']') /* right bracket can appear as collating symbol */ + wpat++; + in_collsym = 1; + } + else if (in_collsym && wc == L'.' && *wpat == L']') + { + wpat++; + in_collsym = 0; + } + else if (wc == L'[' && *wpat == L'=') /* equivalence class */ + { + wpat++; + if (*wpat == L']') /* right bracket can appear as equivalence class */ + wpat++; + in_equiv = 1; + } + else if (in_equiv && wc == L'=' && *wpat == L']') + { + wpat++; + in_equiv = 0; + } + } + while ((wc = *wpat++) != L']'); + matlen++; /* bracket expression can only match one char */ + break; + } + } + + return matlen; +} +#endif + +/* Return 1 of the first character of STRING could match the first + character of pattern PAT. Used to avoid n2 calls to strmatch(). */ +int +match_pattern_char (pat, string) + char *pat, *string; +{ + char c; + + if (*string == 0) + return (0); + + switch (c = *pat++) + { + default: + return (*string == c); + case '\\': + return (*string == *pat); + case '?': + return (*pat == LPAREN ? 1 : (*string != '\0')); + case '*': + return (1); + case '+': + case '!': + case '@': + return (*pat == LPAREN ? 1 : (*string == c)); + case '[': + return (*string != '\0'); + } +} + +int +umatchlen (pat, max) + char *pat; + size_t max; +{ + char c, *brack; + int matlen, t, in_cclass, in_collsym, in_equiv; + + if (*pat == 0) + return (0); + + matlen = 0; + while (c = *pat++) + { + switch (c) + { + default: + matlen++; + break; + case L'\\': + if (*pat == 0) + return ++matlen; + else + { + matlen++; + pat++; + } + break; + case L'?': + if (*pat == LPAREN) + return (matlen = -1); /* XXX for now */ + else + matlen++; + break; + case L'*': + return (matlen = -1); + case L'+': + case L'!': + case L'@': + if (*pat == LPAREN) + return (matlen = -1); /* XXX for now */ + else + matlen++; + break; + case L'[': + /* scan for ending `]', skipping over embedded [:...:] */ + brack = pat; + c = *pat++; + do + { + if (c == 0) + { + matlen += pat - brack - 1; /* incremented below */ + break; + } + else if (c == '\\') + { + c = *pat++; + if (*pat == 0) + break; + } + else if (c == '[' && *pat == ':') /* character class */ + { + pat++; + in_cclass = 1; + } + else if (in_cclass && c == ':' && *pat == ']') + { + pat++; + in_cclass = 0; + } + else if (c == '[' && *pat == '.') /* collating symbol */ + { + pat++; + if (*pat == ']') /* right bracket can appear as collating symbol */ + pat++; + in_collsym = 1; + } + else if (in_collsym && c == '.' && *pat == ']') + { + pat++; + in_collsym = 0; + } + else if (c == '[' && *pat == '=') /* equivalence class */ + { + pat++; + if (*pat == ']') /* right bracket can appear as equivalence class */ + pat++; + in_equiv = 1; + } + else if (in_equiv && c == '=' && *pat == ']') + { + pat++; + in_equiv = 0; + } + } + while ((c = *pat++) != ']'); + matlen++; /* bracket expression can only match one char */ + break; + } + } + + return matlen; +} diff --git a/lib/glob/gmisc.c~ b/lib/glob/gmisc.c~ new file mode 100644 index 00000000..2de3b7b1 --- /dev/null +++ b/lib/glob/gmisc.c~ @@ -0,0 +1,311 @@ +/* gmisc.c -- miscellaneous pattern matching utility functions for Bash. + + Copyright (C) 2010 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne-Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <config.h> + +#include "bashtypes.h" + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" +#include "shmbutil.h" + +#include "stdc.h" + +#ifndef RPAREN +# define RPAREN '(' +#endif +#ifndef LPAREN +# define LPAREN ')' +#endif + +#if defined (HANDLE_MULTIBYTE) +/* Return 1 of the first character of WSTRING could match the first + character of pattern WPAT. Wide character version. */ +int +match_pattern_wchar (wpat, wstring) + wchar_t *wpat, *wstring; +{ + wchar_t wc; + + if (*wstring == 0) + return (0); + + switch (wc = *wpat++) + { + default: + return (*wstring == wc); + case L'\\': + return (*wstring == *wpat); + case L'?': + return (*wpat == LPAREN ? 1 : (*wstring != L'\0')); + case L'*': + return (1); + case L'+': + case L'!': + case L'@': + return (*wpat == LPAREN ? 1 : (*wstring == wc)); + case L'[': + return (*wstring != L'\0'); + } +} + +int +wmatchlen (wpat, wmax) + wchar_t *wpat; + size_t wmax; +{ + wchar_t wc, *wbrack; + int matlen, t, in_cclass, in_collsym, in_equiv; + + if (*wpat == 0) + return (0); + + matlen = 0; + while (wc = *wpat++) + { + switch (wc) + { + default: + matlen++; + break; + case L'\\': + if (*wpat == 0) + return ++matlen; + else + { + matlen++; + wpat++; + } + break; + case L'?': + if (*wpat == LPAREN) + return (matlen = -1); /* XXX for now */ + else + matlen++; + break; + case L'*': + return (matlen = -1); + case L'+': + case L'!': + case L'@': + if (*wpat == LPAREN) + return (matlen = -1); /* XXX for now */ + else + matlen++; + break; + case L'[': + /* scan for ending `]', skipping over embedded [:...:] */ + wbrack = wpat; + wc = *wpat++; + do + { + if (wc == 0) + { + matlen += wpat - wbrack - 1; /* incremented below */ + break; + } + else if (wc == L'\\') + { + wc = *wpat++; + if (*wpat == 0) + break; + } + else if (wc == L'[' && *wpat == L':') /* character class */ + { + wpat++; + in_cclass = 1; + } + else if (in_cclass && wc == L':' && *wpat == L']') + { + wpat++; + in_cclass = 0; + } + else if (wc == L'[' && *wpat == L'.') /* collating symbol */ + { + wpat++; + if (*wpat == L']') /* right bracket can appear as collating symbol */ + wpat++; + in_collsym = 1; + } + else if (in_collsym && wc == L'.' && *wpat == L']') + { + wpat++; + in_collsym = 0; + } + else if (wc == L'[' && *wpat == L'=') /* equivalence class */ + { + wpat++; + if (*wpat == L']') /* right bracket can appear as equivalence class */ + wpat++; + in_equiv = 1; + } + else if (in_equiv && wc == L'=' && *wpat == L']') + { + wpat++; + in_equiv = 0; + } + } + while ((wc = *wpat++) != L']'); + matlen++; /* bracket expression can only match one char */ + break; + } + } + + return matlen; +} +#endif + +/* Return 1 of the first character of STRING could match the first + character of pattern PAT. Used to avoid n2 calls to strmatch(). */ +int +match_pattern_char (pat, string) + char *pat, *string; +{ + char c; + + if (*string == 0) + return (0); + + switch (c = *pat++) + { + default: + return (*string == c); + case '\\': + return (*string == *pat); + case '?': + return (*pat == LPAREN ? 1 : (*string != '\0')); + case '*': + return (1); + case '+': + case '!': + case '@': + return (*pat == LPAREN ? 1 : (*string == c)); + case '[': + return (*string != '\0'); + } +} + +int +umatchlen (pat, max) + char *pat; + size_t max; +{ + char c, *brack; + int matlen, t, in_cclass, in_collsym, in_equiv; + + if (*pat == 0) + return (0); + + matlen = 0; + while (c = *pat++) + { + switch (c) + { + default: + matlen++; + break; + case L'\\': + if (*pat == 0) + return ++matlen; + else + { + matlen++; + pat++; + } + break; + case L'?': + if (*pat == LPAREN) + return (matlen = max); /* XXX for now */ + else + matlen++; + break; + case L'*': + return (matlen = max); + case L'+': + case L'!': + case L'@': + if (*pat == LPAREN) + return (matlen = max); /* XXX for now */ + else + matlen++; + break; + case L'[': + /* scan for ending `]', skipping over embedded [:...:] */ + brack = pat; + c = *pat++; + do + { + if (c == 0) + { + matlen += pat - brack - 1; /* incremented below */ + break; + } + else if (c == '\\') + { + c = *pat++; + if (*pat == 0) + break; + } + else if (c == '[' && *pat == ':') /* character class */ + { + pat++; + in_cclass = 1; + } + else if (in_cclass && c == ':' && *pat == ']') + { + pat++; + in_cclass = 0; + } + else if (c == '[' && *pat == '.') /* collating symbol */ + { + pat++; + if (*pat == ']') /* right bracket can appear as collating symbol */ + pat++; + in_collsym = 1; + } + else if (in_collsym && c == '.' && *pat == ']') + { + pat++; + in_collsym = 0; + } + else if (c == '[' && *pat == '=') /* equivalence class */ + { + pat++; + if (*pat == ']') /* right bracket can appear as equivalence class */ + pat++; + in_equiv = 1; + } + else if (in_equiv && c == '=' && *pat == ']') + { + pat++; + in_equiv = 0; + } + } + while ((c = *pat++) != ']'); + matlen++; /* bracket expression can only match one char */ + break; + } + } + + return matlen; +} diff --git a/lib/readline/complete.c b/lib/readline/complete.c index 00186cc2..7501978f 100644 --- a/lib/readline/complete.c +++ b/lib/readline/complete.c @@ -2092,8 +2092,8 @@ complete_fncmp (convfn, convlen, filename, filename_len) return 1; if (convlen < filename_len) return 0; - s1 = convfn; - s2 = filename; + s1 = (char *)convfn; + s2 = (char *)filename; len = filename_len; do { diff --git a/lib/readline/doc/history.3 b/lib/readline/doc/history.3 index 44d8cf3f..788ef042 100644 --- a/lib/readline/doc/history.3 +++ b/lib/readline/doc/history.3 @@ -6,9 +6,9 @@ .\" Case Western Reserve University .\" chet@ins.CWRU.Edu .\" -.\" Last Change: Thu Jul 31 08:46:08 EDT 2003 +.\" Last Change: Thu Aug 12 22:24:41 EDT 2010 .\" -.TH HISTORY 3 "2003 July 31" "GNU History 6.0" +.TH HISTORY 3 "2010 August 12" "GNU History 6.2" .\" .\" File Name macro. This used to be `.PN', for Path Name, .\" but Sun doesn't seem to like that very much. @@ -40,8 +40,8 @@ .SH NAME history \- GNU History Library .SH COPYRIGHT -.if t The GNU History Library is Copyright \(co 1989-2002 by the Free Software Foundation, Inc. -.if n The GNU History Library is Copyright (C) 1989-2002 by the Free Software Foundation, Inc. +.if t The GNU History Library is Copyright \(co 1989-2010 by the Free Software Foundation, Inc. +.if n The GNU History Library is Copyright (C) 1989-2010 by the Free Software Foundation, Inc. .SH DESCRIPTION Many programs read input from the user a line at a time. The GNU History library is able to keep track of those lines, associate arbitrary @@ -83,6 +83,8 @@ the history expansion character. .PP An event designator is a reference to a command line entry in the history list. +Unless the reference is absolute, events are relative to the current +position in the history list. .PP .PD 0 .TP @@ -96,18 +98,22 @@ Refer to command line .IR n . .TP .B !\-\fIn\fR -Refer to the current command line minus +Refer to the current command minus .IR n . .TP .B !! Refer to the previous command. This is a synonym for `!\-1'. .TP .B !\fIstring\fR -Refer to the most recent command starting with +Refer to the most recent command +preceding the current position in the history list +starting with .IR string . .TP .B !?\fIstring\fR\fB[?]\fR -Refer to the most recent command containing +Refer to the most recent command +preceding the current postition in the history list +containing .IR string . The trailing \fB?\fP may be omitted if .I string @@ -569,10 +575,13 @@ The number of entries currently stored in the history list. The maximum number of history entries. This must be changed using \fBstifle_history()\fP. -.Vb int history_write_timestamps +.Vb int history_wite_timestamps If non-zero, timestamps are written to the history file, so they can be preserved between sessions. The default value is 0, meaning that timestamps are not saved. +The current timestamp format uses the value of \fIhistory_comment_char\fP +to delimit timestamp entries in the history file. If that variable does +not have a value (the default), timestamps will not be written. .Vb char history_expansion_char The character that introduces a history event. The default is \fB!\fP. diff --git a/lib/readline/doc/history.3~ b/lib/readline/doc/history.3~ new file mode 100644 index 00000000..5256cc5c --- /dev/null +++ b/lib/readline/doc/history.3~ @@ -0,0 +1,672 @@ +.\" +.\" MAN PAGE COMMENTS to +.\" +.\" Chet Ramey +.\" Information Network Services +.\" Case Western Reserve University +.\" chet@ins.CWRU.Edu +.\" +.\" Last Change: Tue Aug 3 15:23:38 EDT 2010 +.\" +.TH HISTORY 3 "2010 August 3" "GNU History 6.2" +.\" +.\" File Name macro. This used to be `.PN', for Path Name, +.\" but Sun doesn't seem to like that very much. +.\" +.de FN +\fI\|\\$1\|\fP +.. +.ds lp \fR\|(\fP +.ds rp \fR\|)\fP +.\" FnN return-value fun-name N arguments +.de Fn1 +\fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3\fP\\*(rp +.br +.. +.de Fn2 +.if t \fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3,\|\\$4\fP\\*(rp +.if n \fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3, \\$4\fP\\*(rp +.br +.. +.de Fn3 +.if t \fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3,\|\\$4,\|\\$5\fP\|\\*(rp +.if n \fI\\$1\fP \fB\\$2\fP \\*(lp\fI\\$3, \\$4, \\$5\fP\\*(rp +.br +.. +.de Vb +\fI\\$1\fP \fB\\$2\fP +.br +.. +.SH NAME +history \- GNU History Library +.SH COPYRIGHT +.if t The GNU History Library is Copyright \(co 1989-2010 by the Free Software Foundation, Inc. +.if n The GNU History Library is Copyright (C) 1989-2010 by the Free Software Foundation, Inc. +.SH DESCRIPTION +Many programs read input from the user a line at a time. The GNU +History library is able to keep track of those lines, associate arbitrary +data with each line, and utilize information from previous lines in +composing new ones. +.PP +.SH "HISTORY EXPANSION" +.PP +The history library supports a history expansion feature that +is identical to the history expansion in +.BR bash. +This section describes what syntax features are available. +.PP +History expansions introduce words from the history list into +the input stream, making it easy to repeat commands, insert the +arguments to a previous command into the current input line, or +fix errors in previous commands quickly. +.PP +History expansion is usually performed immediately after a complete line +is read. +It takes place in two parts. +The first is to determine which line from the history list +to use during substitution. +The second is to select portions of that line for inclusion into +the current one. +The line selected from the history is the \fIevent\fP, +and the portions of that line that are acted upon are \fIwords\fP. +Various \fImodifiers\fP are available to manipulate the selected words. +The line is broken into words in the same fashion as \fBbash\fP +does when reading input, +so that several words that would otherwise be separated +are considered one word when surrounded by quotes (see the +description of \fBhistory_tokenize()\fP below). +History expansions are introduced by the appearance of the +history expansion character, which is \^\fB!\fP\^ by default. +Only backslash (\^\fB\e\fP\^) and single quotes can quote +the history expansion character. +.SS Event Designators +.PP +An event designator is a reference to a command line entry in the +history list. +Unless the reference is absolute, events are relative to the current +position in the history list. +.PP +.PD 0 +.TP +.B ! +Start a history substitution, except when followed by a +.BR blank , +newline, = or (. +.TP +.B !\fIn\fR +Refer to command line +.IR n . +.TP +.B !\-\fIn\fR +Refer to the current command minus +.IR n . +.TP +.B !! +Refer to the previous command. This is a synonym for `!\-1'. +.TP +.B !\fIstring\fR +Refer to the most recent command +preceding the current position in the history list +starting with +.IR string . +.TP +.B !?\fIstring\fR\fB[?]\fR +Refer to the most recent command +preceding the current postition in the history list +containing +.IR string . +The trailing \fB?\fP may be omitted if +.I string +is followed immediately by a newline. +.TP +.B \d\s+2^\s-2\u\fIstring1\fP\d\s+2^\s-2\u\fIstring2\fP\d\s+2^\s-2\u +Quick substitution. Repeat the last command, replacing +.I string1 +with +.IR string2 . +Equivalent to +``!!:s/\fIstring1\fP/\fIstring2\fP/'' +(see \fBModifiers\fP below). +.TP +.B !# +The entire command line typed so far. +.PD +.SS Word Designators +.PP +Word designators are used to select desired words from the event. +A +.B : +separates the event specification from the word designator. +It may be omitted if the word designator begins with a +.BR ^ , +.BR $ , +.BR * , +.BR \- , +or +.BR % . +Words are numbered from the beginning of the line, +with the first word being denoted by 0 (zero). +Words are inserted into the current line separated by single spaces. +.PP +.PD 0 +.TP +.B 0 (zero) +The zeroth word. For the shell, this is the command +word. +.TP +.I n +The \fIn\fRth word. +.TP +.B ^ +The first argument. That is, word 1. +.TP +.B $ +The last argument. +.TP +.B % +The word matched by the most recent `?\fIstring\fR?' search. +.TP +.I x\fB\-\fPy +A range of words; `\-\fIy\fR' abbreviates `0\-\fIy\fR'. +.TP +.B * +All of the words but the zeroth. This is a synonym +for `\fI1\-$\fP'. It is not an error to use +.B * +if there is just one +word in the event; the empty string is returned in that case. +.TP +.B x* +Abbreviates \fIx\-$\fP. +.TP +.B x\- +Abbreviates \fIx\-$\fP like \fBx*\fP, but omits the last word. +.PD +.PP +If a word designator is supplied without an event specification, the +previous command is used as the event. +.SS Modifiers +.PP +After the optional word designator, there may appear a sequence of +one or more of the following modifiers, each preceded by a `:'. +.PP +.PD 0 +.PP +.TP +.B h +Remove a trailing file name component, leaving only the head. +.TP +.B t +Remove all leading file name components, leaving the tail. +.TP +.B r +Remove a trailing suffix of the form \fI.xxx\fP, leaving the +basename. +.TP +.B e +Remove all but the trailing suffix. +.TP +.B p +Print the new command but do not execute it. +.TP +.B q +Quote the substituted words, escaping further substitutions. +.TP +.B x +Quote the substituted words as with +.BR q , +but break into words at +.B blanks +and newlines. +.TP +.B s/\fIold\fP/\fInew\fP/ +Substitute +.I new +for the first occurrence of +.I old +in the event line. Any delimiter can be used in place of /. The +final delimiter is optional if it is the last character of the +event line. The delimiter may be quoted in +.I old +and +.I new +with a single backslash. If & appears in +.IR new , +it is replaced by +.IR old . +A single backslash will quote the &. If +.I old +is null, it is set to the last +.I old +substituted, or, if no previous history substitutions took place, +the last +.I string +in a +.B !?\fIstring\fR\fB[?]\fR +search. +.TP +.B & +Repeat the previous substitution. +.TP +.B g +Cause changes to be applied over the entire event line. This is +used in conjunction with `\fB:s\fP' (e.g., `\fB:gs/\fIold\fP/\fInew\fP/\fR') +or `\fB:&\fP'. If used with +`\fB:s\fP', any delimiter can be used +in place of /, and the final delimiter is optional +if it is the last character of the event line. +An \fBa\fP may be used as a synonym for \fBg\fP. +.TP +.B G +Apply the following `\fBs\fP' modifier once to each word in the event line. +.PD +.SH "PROGRAMMING WITH HISTORY FUNCTIONS" +This section describes how to use the History library in other programs. +.SS Introduction to History +.PP +The programmer using the History library has available functions +for remembering lines on a history list, associating arbitrary data +with a line, removing lines from the list, searching through the list +for a line containing an arbitrary text string, and referencing any line +in the list directly. In addition, a history \fIexpansion\fP function +is available which provides for a consistent user interface across +different programs. +.PP +The user using programs written with the History library has the +benefit of a consistent user interface with a set of well-known +commands for manipulating the text of previous lines and using that text +in new commands. The basic history manipulation commands are +identical to +the history substitution provided by \fBbash\fP. +.PP +If the programmer desires, he can use the Readline library, which +includes some history manipulation by default, and has the added +advantage of command line editing. +.PP +Before declaring any functions using any functionality the History +library provides in other code, an application writer should include +the file +.FN <readline/history.h> +in any file that uses the +History library's features. It supplies extern declarations for all +of the library's public functions and variables, and declares all of +the public data structures. + +.SS History Storage +.PP +The history list is an array of history entries. A history entry is +declared as follows: +.PP +.Vb "typedef void *" histdata_t; +.PP +.nf +typedef struct _hist_entry { + char *line; + char *timestamp; + histdata_t data; +} HIST_ENTRY; +.fi +.PP +The history list itself might therefore be declared as +.PP +.Vb "HIST_ENTRY **" the_history_list; +.PP +The state of the History library is encapsulated into a single structure: +.PP +.nf +/* + * A structure used to pass around the current state of the history. + */ +typedef struct _hist_state { + HIST_ENTRY **entries; /* Pointer to the entries themselves. */ + int offset; /* The location pointer within this array. */ + int length; /* Number of elements within this array. */ + int size; /* Number of slots allocated to this array. */ + int flags; +} HISTORY_STATE; +.fi +.PP +If the flags member includes \fBHS_STIFLED\fP, the history has been +stifled. +.SH "History Functions" +.PP +This section describes the calling sequence for the various functions +exported by the GNU History library. +.SS Initializing History and State Management +This section describes functions used to initialize and manage +the state of the History library when you want to use the history +functions in your program. + +.Fn1 void using_history void +Begin a session in which the history functions might be used. This +initializes the interactive variables. + +.Fn1 "HISTORY_STATE *" history_get_history_state void +Return a structure describing the current state of the input history. + +.Fn1 void history_set_history_state "HISTORY_STATE *state" +Set the state of the history list according to \fIstate\fP. + +.SS History List Management + +These functions manage individual entries on the history list, or set +parameters managing the list itself. + +.Fn1 void add_history "const char *string" +Place \fIstring\fP at the end of the history list. The associated data +field (if any) is set to \fBNULL\fP. + +.Fn1 void add_history_time "const char *string" +Change the time stamp associated with the most recent history entry to +\fIstring\fP. + +.Fn1 "HIST_ENTRY *" remove_history "int which" +Remove history entry at offset \fIwhich\fP from the history. The +removed element is returned so you can free the line, data, +and containing structure. + +.Fn1 "histdata_t" free_history_entry "HIST_ENTRY *histent" +Free the history entry \fIhistent\fP and any history library private +data associated with it. Returns the application-specific data +so the caller can dispose of it. + +.Fn3 "HIST_ENTRY *" replace_history_entry "int which" "const char *line" "histdata_t data" +Make the history entry at offset \fIwhich\fP have \fIline\fP and \fIdata\fP. +This returns the old entry so the caller can dispose of any +application-specific data. In the case +of an invalid \fIwhich\fP, a \fBNULL\fP pointer is returned. + +.Fn1 void clear_history "void" +Clear the history list by deleting all the entries. + +.Fn1 void stifle_history "int max" +Stifle the history list, remembering only the last \fImax\fP entries. + +.Fn1 int unstifle_history "void" +Stop stifling the history. This returns the previously-set +maximum number of history entries (as set by \fBstifle_history()\fP). +history was stifled. The value is positive if the history was +stifled, negative if it wasn't. + +.Fn1 int history_is_stifled "void" +Returns non-zero if the history is stifled, zero if it is not. + +.SS Information About the History List + +These functions return information about the entire history list or +individual list entries. + +.Fn1 "HIST_ENTRY **" history_list "void" +Return a \fBNULL\fP terminated array of \fIHIST_ENTRY *\fP which is the +current input history. Element 0 of this list is the beginning of time. +If there is no history, return \fBNULL\fP. + +.Fn1 int where_history "void" +Returns the offset of the current history element. + +.Fn1 "HIST_ENTRY *" current_history "void" +Return the history entry at the current position, as determined by +\fBwhere_history()\fP. If there is no entry there, return a \fBNULL\fP +pointer. + +.Fn1 "HIST_ENTRY *" history_get "int offset" +Return the history entry at position \fIoffset\fP, starting from +\fBhistory_base\fP. +If there is no entry there, or if \fIoffset\fP +is greater than the history length, return a \fBNULL\fP pointer. + +.Fn1 "time_t" history_get_time "HIST_ENTRY *" +Return the time stamp associated with the history entry passed as the argument. + +.Fn1 int history_total_bytes "void" +Return the number of bytes that the primary history entries are using. +This function returns the sum of the lengths of all the lines in the +history. + +.SS Moving Around the History List + +These functions allow the current index into the history list to be +set or changed. + +.Fn1 int history_set_pos "int pos" +Set the current history offset to \fIpos\fP, an absolute index +into the list. +Returns 1 on success, 0 if \fIpos\fP is less than zero or greater +than the number of history entries. + +.Fn1 "HIST_ENTRY *" previous_history "void" +Back up the current history offset to the previous history entry, and +return a pointer to that entry. If there is no previous entry, return +a \fBNULL\fP pointer. + +.Fn1 "HIST_ENTRY *" next_history "void" +Move the current history offset forward to the next history entry, and +return the a pointer to that entry. If there is no next entry, return +a \fBNULL\fP pointer. + +.SS Searching the History List + +These functions allow searching of the history list for entries containing +a specific string. Searching may be performed both forward and backward +from the current history position. The search may be \fIanchored\fP, +meaning that the string must match at the beginning of the history entry. + +.Fn2 int history_search "const char *string" "int direction" +Search the history for \fIstring\fP, starting at the current history offset. +If \fIdirection\fP is less than 0, then the search is through +previous entries, otherwise through subsequent entries. +If \fIstring\fP is found, then +the current history index is set to that history entry, and the value +returned is the offset in the line of the entry where +\fIstring\fP was found. Otherwise, nothing is changed, and a -1 is +returned. + +.Fn2 int history_search_prefix "const char *string" "int direction" +Search the history for \fIstring\fP, starting at the current history +offset. The search is anchored: matching lines must begin with +\fIstring\fP. If \fIdirection\fP is less than 0, then the search is +through previous entries, otherwise through subsequent entries. +If \fIstring\fP is found, then the +current history index is set to that entry, and the return value is 0. +Otherwise, nothing is changed, and a -1 is returned. + +.Fn3 int history_search_pos "const char *string" "int direction" "int pos" +Search for \fIstring\fP in the history list, starting at \fIpos\fP, an +absolute index into the list. If \fIdirection\fP is negative, the search +proceeds backward from \fIpos\fP, otherwise forward. Returns the absolute +index of the history element where \fIstring\fP was found, or -1 otherwise. + +.SS Managing the History File +The History library can read the history from and write it to a file. +This section documents the functions for managing a history file. + +.Fn1 int read_history "const char *filename" +Add the contents of \fIfilename\fP to the history list, a line at a time. +If \fIfilename\fP is \fBNULL\fP, then read from \fI~/.history\fP. +Returns 0 if successful, or \fBerrno\fP if not. + +.Fn3 int read_history_range "const char *filename" "int from" "int to" +Read a range of lines from \fIfilename\fP, adding them to the history list. +Start reading at line \fIfrom\fP and end at \fIto\fP. +If \fIfrom\fP is zero, start at the beginning. If \fIto\fP is less than +\fIfrom\fP, then read until the end of the file. If \fIfilename\fP is +\fBNULL\fP, then read from \fI~/.history\fP. Returns 0 if successful, +or \fBerrno\fP if not. + +.Fn1 int write_history "const char *filename" +Write the current history to \fIfilename\fP, overwriting \fIfilename\fP +if necessary. +If \fIfilename\fP is \fBNULL\fP, then write the history list to \fI~/.history\fP. +Returns 0 on success, or \fBerrno\fP on a read or write error. + + +.Fn2 int append_history "int nelements" "const char *filename" +Append the last \fInelements\fP of the history list to \fIfilename\fP. +If \fIfilename\fP is \fBNULL\fP, then append to \fI~/.history\fP. +Returns 0 on success, or \fBerrno\fP on a read or write error. + +.Fn2 int history_truncate_file "const char *filename" "int nlines" +Truncate the history file \fIfilename\fP, leaving only the last +\fInlines\fP lines. +If \fIfilename\fP is \fBNULL\fP, then \fI~/.history\fP is truncated. +Returns 0 on success, or \fBerrno\fP on failure. + +.SS History Expansion + +These functions implement history expansion. + +.Fn2 int history_expand "char *string" "char **output" +Expand \fIstring\fP, placing the result into \fIoutput\fP, a pointer +to a string. Returns: +.RS +.PD 0 +.TP +0 +If no expansions took place (or, if the only change in +the text was the removal of escape characters preceding the history expansion +character); +.TP +1 +if expansions did take place; +.TP +-1 +if there was an error in expansion; +.TP +2 +if the returned line should be displayed, but not executed, +as with the \fB:p\fP modifier. +.PD +.RE +If an error ocurred in expansion, then \fIoutput\fP contains a descriptive +error message. + +.Fn3 "char *" get_history_event "const char *string" "int *cindex" "int qchar" +Returns the text of the history event beginning at \fIstring\fP + +\fI*cindex\fP. \fI*cindex\fP is modified to point to after the event +specifier. At function entry, \fIcindex\fP points to the index into +\fIstring\fP where the history event specification begins. \fIqchar\fP +is a character that is allowed to end the event specification in addition +to the ``normal'' terminating characters. + +.Fn1 "char **" history_tokenize "const char *string" +Return an array of tokens parsed out of \fIstring\fP, much as the +shell might. +The tokens are split on the characters in the +\fBhistory_word_delimiters\fP variable, +and shell quoting conventions are obeyed. + +.Fn3 "char *" history_arg_extract "int first" "int last" "const char *string" +Extract a string segment consisting of the \fIfirst\fP through \fIlast\fP +arguments present in \fIstring\fP. Arguments are split using +\fBhistory_tokenize()\fP. + +.SS History Variables + +This section describes the externally-visible variables exported by +the GNU History Library. + +.Vb int history_base +The logical offset of the first entry in the history list. + +.Vb int history_length +The number of entries currently stored in the history list. + +.Vb int history_max_entries +The maximum number of history entries. This must be changed using +\fBstifle_history()\fP. + +.Vb int history_wite_timestamps +If non-zero, timestamps are written to the history file, so they can be +preserved between sessions. The default value is 0, meaning that +timestamps are not saved. +The current timestamp format uses the value of \fIhistory_comment_char\fP +to delimit timestamp entries in the history file. If that variable does +not have a value (the default), timestamps will not be written. + +.Vb char history_expansion_char +The character that introduces a history event. The default is \fB!\fP. +Setting this to 0 inhibits history expansion. + +.Vb char history_subst_char +The character that invokes word substitution if found at the start of +a line. The default is \fB^\fP. + +.Vb char history_comment_char +During tokenization, if this character is seen as the first character +of a word, then it and all subsequent characters up to a newline are +ignored, suppressing history expansion for the remainder of the line. +This is disabled by default. + +.Vb "char *" history_word_delimiters +The characters that separate tokens for \fBhistory_tokenize()\fP. +The default value is \fB"\ \et\en()<>;&|"\fP. + +.Vb "char *" history_no_expand_chars +The list of characters which inhibit history expansion if found immediately +following \fBhistory_expansion_char\fP. The default is space, tab, newline, +\fB\er\fP, and \fB=\fP. + +.Vb "char *" history_search_delimiter_chars +The list of additional characters which can delimit a history search +string, in addition to space, tab, \fI:\fP and \fI?\fP in the case of +a substring search. The default is empty. + +.Vb int history_quotes_inhibit_expansion +If non-zero, single-quoted words are not scanned for the history expansion +character. The default value is 0. + +.Vb "rl_linebuf_func_t *" history_inhibit_expansion_function +This should be set to the address of a function that takes two arguments: +a \fBchar *\fP (\fIstring\fP) +and an \fBint\fP index into that string (\fIi\fP). +It should return a non-zero value if the history expansion starting at +\fIstring[i]\fP should not be performed; zero if the expansion should +be done. +It is intended for use by applications like \fBbash\fP that use the history +expansion character for additional purposes. +By default, this variable is set to \fBNULL\fP. +.SH FILES +.PD 0 +.TP +.FN ~/.history +Default filename for reading and writing saved history +.PD +.SH "SEE ALSO" +.PD 0 +.TP +\fIThe Gnu Readline Library\fP, Brian Fox and Chet Ramey +.TP +\fIThe Gnu History Library\fP, Brian Fox and Chet Ramey +.TP +\fIbash\fP(1) +.TP +\fIreadline\fP(3) +.PD +.SH AUTHORS +Brian Fox, Free Software Foundation +.br +bfox@gnu.org +.PP +Chet Ramey, Case Western Reserve University +.br +chet@ins.CWRU.Edu +.SH BUG REPORTS +If you find a bug in the +.B history +library, you should report it. But first, you should +make sure that it really is a bug, and that it appears in the latest +version of the +.B history +library that you have. +.PP +Once you have determined that a bug actually exists, mail a +bug report to \fIbug\-readline\fP@\fIgnu.org\fP. +If you have a fix, you are welcome to mail that +as well! Suggestions and `philosophical' bug reports may be mailed +to \fPbug-readline\fP@\fIgnu.org\fP or posted to the Usenet +newsgroup +.BR gnu.bash.bug . +.PP +Comments and bug reports concerning +this manual page should be directed to +.IR chet@ins.CWRU.Edu . diff --git a/lib/readline/doc/hstech.texi b/lib/readline/doc/hstech.texi index c4e5a752..ad9cfa82 100644 --- a/lib/readline/doc/hstech.texi +++ b/lib/readline/doc/hstech.texi @@ -426,6 +426,10 @@ The maximum number of history entries. This must be changed using If non-zero, timestamps are written to the history file, so they can be preserved between sessions. The default value is 0, meaning that timestamps are not saved. + +The current timestamp format uses the value of @var{history_comment_char} +to delimit timestamp entries in the history file. If that variable does +not have a value (the default), timestamps will not be written. @end deftypevar @deftypevar char history_expansion_char diff --git a/lib/readline/doc/hstech.texi~ b/lib/readline/doc/hstech.texi~ new file mode 100644 index 00000000..c4e5a752 --- /dev/null +++ b/lib/readline/doc/hstech.texi~ @@ -0,0 +1,573 @@ +@ignore +This file documents the user interface to the GNU History library. + +Copyright (C) 1988-2007 Free Software Foundation, Inc. +Authored by Brian Fox and Chet Ramey. + +Permission is granted to make and distribute verbatim copies of this manual +provided the copyright notice and this permission notice are preserved on +all copies. + +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +GNU Copyright statement is available to the distributee, and provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ignore + +@node Programming with GNU History +@chapter Programming with GNU History + +This chapter describes how to interface programs that you write +with the @sc{gnu} History Library. +It should be considered a technical guide. +For information on the interactive use of @sc{gnu} History, @pxref{Using +History Interactively}. + +@menu +* Introduction to History:: What is the GNU History library for? +* History Storage:: How information is stored. +* History Functions:: Functions that you can use. +* History Variables:: Variables that control behaviour. +* History Programming Example:: Example of using the GNU History Library. +@end menu + +@node Introduction to History +@section Introduction to History + +Many programs read input from the user a line at a time. The @sc{gnu} +History library is able to keep track of those lines, associate arbitrary +data with each line, and utilize information from previous lines in +composing new ones. + +The programmer using the History library has available functions +for remembering lines on a history list, associating arbitrary data +with a line, removing lines from the list, searching through the list +for a line containing an arbitrary text string, and referencing any line +in the list directly. In addition, a history @dfn{expansion} function +is available which provides for a consistent user interface across +different programs. + +The user using programs written with the History library has the +benefit of a consistent user interface with a set of well-known +commands for manipulating the text of previous lines and using that text +in new commands. The basic history manipulation commands are similar to +the history substitution provided by @code{csh}. + +If the programmer desires, he can use the Readline library, which +includes some history manipulation by default, and has the added +advantage of command line editing. + +Before declaring any functions using any functionality the History +library provides in other code, an application writer should include +the file @code{<readline/history.h>} in any file that uses the +History library's features. It supplies extern declarations for all +of the library's public functions and variables, and declares all of +the public data structures. + +@node History Storage +@section History Storage + +The history list is an array of history entries. A history entry is +declared as follows: + +@example +typedef void *histdata_t; + +typedef struct _hist_entry @{ + char *line; + char *timestamp; + histdata_t data; +@} HIST_ENTRY; +@end example + +The history list itself might therefore be declared as + +@example +HIST_ENTRY **the_history_list; +@end example + +The state of the History library is encapsulated into a single structure: + +@example +/* + * A structure used to pass around the current state of the history. + */ +typedef struct _hist_state @{ + HIST_ENTRY **entries; /* Pointer to the entries themselves. */ + int offset; /* The location pointer within this array. */ + int length; /* Number of elements within this array. */ + int size; /* Number of slots allocated to this array. */ + int flags; +@} HISTORY_STATE; +@end example + +If the flags member includes @code{HS_STIFLED}, the history has been +stifled. + +@node History Functions +@section History Functions + +This section describes the calling sequence for the various functions +exported by the @sc{gnu} History library. + +@menu +* Initializing History and State Management:: Functions to call when you + want to use history in a + program. +* History List Management:: Functions used to manage the list + of history entries. +* Information About the History List:: Functions returning information about + the history list. +* Moving Around the History List:: Functions used to change the position + in the history list. +* Searching the History List:: Functions to search the history list + for entries containing a string. +* Managing the History File:: Functions that read and write a file + containing the history list. +* History Expansion:: Functions to perform csh-like history + expansion. +@end menu + +@node Initializing History and State Management +@subsection Initializing History and State Management + +This section describes functions used to initialize and manage +the state of the History library when you want to use the history +functions in your program. + +@deftypefun void using_history (void) +Begin a session in which the history functions might be used. This +initializes the interactive variables. +@end deftypefun + +@deftypefun {HISTORY_STATE *} history_get_history_state (void) +Return a structure describing the current state of the input history. +@end deftypefun + +@deftypefun void history_set_history_state (HISTORY_STATE *state) +Set the state of the history list according to @var{state}. +@end deftypefun + +@node History List Management +@subsection History List Management + +These functions manage individual entries on the history list, or set +parameters managing the list itself. + +@deftypefun void add_history (const char *string) +Place @var{string} at the end of the history list. The associated data +field (if any) is set to @code{NULL}. +@end deftypefun + +@deftypefun void add_history_time (const char *string) +Change the time stamp associated with the most recent history entry to +@var{string}. +@end deftypefun + +@deftypefun {HIST_ENTRY *} remove_history (int which) +Remove history entry at offset @var{which} from the history. The +removed element is returned so you can free the line, data, +and containing structure. +@end deftypefun + +@deftypefun {histdata_t} free_history_entry (HIST_ENTRY *histent) +Free the history entry @var{histent} and any history library private +data associated with it. Returns the application-specific data +so the caller can dispose of it. +@end deftypefun + +@deftypefun {HIST_ENTRY *} replace_history_entry (int which, const char *line, histdata_t data) +Make the history entry at offset @var{which} have @var{line} and @var{data}. +This returns the old entry so the caller can dispose of any +application-specific data. In the case +of an invalid @var{which}, a @code{NULL} pointer is returned. +@end deftypefun + +@deftypefun void clear_history (void) +Clear the history list by deleting all the entries. +@end deftypefun + +@deftypefun void stifle_history (int max) +Stifle the history list, remembering only the last @var{max} entries. +@end deftypefun + +@deftypefun int unstifle_history (void) +Stop stifling the history. This returns the previously-set +maximum number of history entries (as set by @code{stifle_history()}). +The value is positive if the history was +stifled, negative if it wasn't. +@end deftypefun + +@deftypefun int history_is_stifled (void) +Returns non-zero if the history is stifled, zero if it is not. +@end deftypefun + +@node Information About the History List +@subsection Information About the History List + +These functions return information about the entire history list or +individual list entries. + +@deftypefun {HIST_ENTRY **} history_list (void) +Return a @code{NULL} terminated array of @code{HIST_ENTRY *} which is the +current input history. Element 0 of this list is the beginning of time. +If there is no history, return @code{NULL}. +@end deftypefun + +@deftypefun int where_history (void) +Returns the offset of the current history element. +@end deftypefun + +@deftypefun {HIST_ENTRY *} current_history (void) +Return the history entry at the current position, as determined by +@code{where_history()}. If there is no entry there, return a @code{NULL} +pointer. +@end deftypefun + +@deftypefun {HIST_ENTRY *} history_get (int offset) +Return the history entry at position @var{offset}, starting from +@code{history_base} (@pxref{History Variables}). +If there is no entry there, or if @var{offset} +is greater than the history length, return a @code{NULL} pointer. +@end deftypefun + +@deftypefun time_t history_get_time (HIST_ENTRY *entry) +Return the time stamp associated with the history entry @var{entry}. +@end deftypefun + +@deftypefun int history_total_bytes (void) +Return the number of bytes that the primary history entries are using. +This function returns the sum of the lengths of all the lines in the +history. +@end deftypefun + +@node Moving Around the History List +@subsection Moving Around the History List + +These functions allow the current index into the history list to be +set or changed. + +@deftypefun int history_set_pos (int pos) +Set the current history offset to @var{pos}, an absolute index +into the list. +Returns 1 on success, 0 if @var{pos} is less than zero or greater +than the number of history entries. +@end deftypefun + +@deftypefun {HIST_ENTRY *} previous_history (void) +Back up the current history offset to the previous history entry, and +return a pointer to that entry. If there is no previous entry, return +a @code{NULL} pointer. +@end deftypefun + +@deftypefun {HIST_ENTRY *} next_history (void) +Move the current history offset forward to the next history entry, and +return the a pointer to that entry. If there is no next entry, return +a @code{NULL} pointer. +@end deftypefun + +@node Searching the History List +@subsection Searching the History List +@cindex History Searching + +These functions allow searching of the history list for entries containing +a specific string. Searching may be performed both forward and backward +from the current history position. The search may be @dfn{anchored}, +meaning that the string must match at the beginning of the history entry. +@cindex anchored search + +@deftypefun int history_search (const char *string, int direction) +Search the history for @var{string}, starting at the current history offset. +If @var{direction} is less than 0, then the search is through +previous entries, otherwise through subsequent entries. +If @var{string} is found, then +the current history index is set to that history entry, and the value +returned is the offset in the line of the entry where +@var{string} was found. Otherwise, nothing is changed, and a -1 is +returned. +@end deftypefun + +@deftypefun int history_search_prefix (const char *string, int direction) +Search the history for @var{string}, starting at the current history +offset. The search is anchored: matching lines must begin with +@var{string}. If @var{direction} is less than 0, then the search is +through previous entries, otherwise through subsequent entries. +If @var{string} is found, then the +current history index is set to that entry, and the return value is 0. +Otherwise, nothing is changed, and a -1 is returned. +@end deftypefun + +@deftypefun int history_search_pos (const char *string, int direction, int pos) +Search for @var{string} in the history list, starting at @var{pos}, an +absolute index into the list. If @var{direction} is negative, the search +proceeds backward from @var{pos}, otherwise forward. Returns the absolute +index of the history element where @var{string} was found, or -1 otherwise. +@end deftypefun + +@node Managing the History File +@subsection Managing the History File + +The History library can read the history from and write it to a file. +This section documents the functions for managing a history file. + +@deftypefun int read_history (const char *filename) +Add the contents of @var{filename} to the history list, a line at a time. +If @var{filename} is @code{NULL}, then read from @file{~/.history}. +Returns 0 if successful, or @code{errno} if not. +@end deftypefun + +@deftypefun int read_history_range (const char *filename, int from, int to) +Read a range of lines from @var{filename}, adding them to the history list. +Start reading at line @var{from} and end at @var{to}. +If @var{from} is zero, start at the beginning. If @var{to} is less than +@var{from}, then read until the end of the file. If @var{filename} is +@code{NULL}, then read from @file{~/.history}. Returns 0 if successful, +or @code{errno} if not. +@end deftypefun + +@deftypefun int write_history (const char *filename) +Write the current history to @var{filename}, overwriting @var{filename} +if necessary. +If @var{filename} is @code{NULL}, then write the history list to +@file{~/.history}. +Returns 0 on success, or @code{errno} on a read or write error. +@end deftypefun + +@deftypefun int append_history (int nelements, const char *filename) +Append the last @var{nelements} of the history list to @var{filename}. +If @var{filename} is @code{NULL}, then append to @file{~/.history}. +Returns 0 on success, or @code{errno} on a read or write error. +@end deftypefun + +@deftypefun int history_truncate_file (const char *filename, int nlines) +Truncate the history file @var{filename}, leaving only the last +@var{nlines} lines. +If @var{filename} is @code{NULL}, then @file{~/.history} is truncated. +Returns 0 on success, or @code{errno} on failure. +@end deftypefun + +@node History Expansion +@subsection History Expansion + +These functions implement history expansion. + +@deftypefun int history_expand (char *string, char **output) +Expand @var{string}, placing the result into @var{output}, a pointer +to a string (@pxref{History Interaction}). Returns: +@table @code +@item 0 +If no expansions took place (or, if the only change in +the text was the removal of escape characters preceding the history expansion +character); +@item 1 +if expansions did take place; +@item -1 +if there was an error in expansion; +@item 2 +if the returned line should be displayed, but not executed, +as with the @code{:p} modifier (@pxref{Modifiers}). +@end table + +If an error ocurred in expansion, then @var{output} contains a descriptive +error message. +@end deftypefun + +@deftypefun {char *} get_history_event (const char *string, int *cindex, int qchar) +Returns the text of the history event beginning at @var{string} + +@var{*cindex}. @var{*cindex} is modified to point to after the event +specifier. At function entry, @var{cindex} points to the index into +@var{string} where the history event specification begins. @var{qchar} +is a character that is allowed to end the event specification in addition +to the ``normal'' terminating characters. +@end deftypefun + +@deftypefun {char **} history_tokenize (const char *string) +Return an array of tokens parsed out of @var{string}, much as the +shell might. The tokens are split on the characters in the +@var{history_word_delimiters} variable, +and shell quoting conventions are obeyed. +@end deftypefun + +@deftypefun {char *} history_arg_extract (int first, int last, const char *string) +Extract a string segment consisting of the @var{first} through @var{last} +arguments present in @var{string}. Arguments are split using +@code{history_tokenize}. +@end deftypefun + +@node History Variables +@section History Variables + +This section describes the externally-visible variables exported by +the @sc{gnu} History Library. + +@deftypevar int history_base +The logical offset of the first entry in the history list. +@end deftypevar + +@deftypevar int history_length +The number of entries currently stored in the history list. +@end deftypevar + +@deftypevar int history_max_entries +The maximum number of history entries. This must be changed using +@code{stifle_history()}. +@end deftypevar + +@deftypevar int history_write_timestamps +If non-zero, timestamps are written to the history file, so they can be +preserved between sessions. The default value is 0, meaning that +timestamps are not saved. +@end deftypevar + +@deftypevar char history_expansion_char +The character that introduces a history event. The default is @samp{!}. +Setting this to 0 inhibits history expansion. +@end deftypevar + +@deftypevar char history_subst_char +The character that invokes word substitution if found at the start of +a line. The default is @samp{^}. +@end deftypevar + +@deftypevar char history_comment_char +During tokenization, if this character is seen as the first character +of a word, then it and all subsequent characters up to a newline are +ignored, suppressing history expansion for the remainder of the line. +This is disabled by default. +@end deftypevar + +@deftypevar {char *} history_word_delimiters +The characters that separate tokens for @code{history_tokenize()}. +The default value is @code{" \t\n()<>;&|"}. +@end deftypevar + +@deftypevar {char *} history_search_delimiter_chars +The list of additional characters which can delimit a history search +string, in addition to space, TAB, @samp{:} and @samp{?} in the case of +a substring search. The default is empty. +@end deftypevar + +@deftypevar {char *} history_no_expand_chars +The list of characters which inhibit history expansion if found immediately +following @var{history_expansion_char}. The default is space, tab, newline, +carriage return, and @samp{=}. +@end deftypevar + +@deftypevar int history_quotes_inhibit_expansion +If non-zero, single-quoted words are not scanned for the history expansion +character. The default value is 0. +@end deftypevar + +@deftypevar {rl_linebuf_func_t *} history_inhibit_expansion_function +This should be set to the address of a function that takes two arguments: +a @code{char *} (@var{string}) +and an @code{int} index into that string (@var{i}). +It should return a non-zero value if the history expansion starting at +@var{string[i]} should not be performed; zero if the expansion should +be done. +It is intended for use by applications like Bash that use the history +expansion character for additional purposes. +By default, this variable is set to @code{NULL}. +@end deftypevar + +@node History Programming Example +@section History Programming Example + +The following program demonstrates simple use of the @sc{gnu} History Library. + +@smallexample +#include <stdio.h> +#include <readline/history.h> + +main (argc, argv) + int argc; + char **argv; +@{ + char line[1024], *t; + int len, done = 0; + + line[0] = 0; + + using_history (); + while (!done) + @{ + printf ("history$ "); + fflush (stdout); + t = fgets (line, sizeof (line) - 1, stdin); + if (t && *t) + @{ + len = strlen (t); + if (t[len - 1] == '\n') + t[len - 1] = '\0'; + @} + + if (!t) + strcpy (line, "quit"); + + if (line[0]) + @{ + char *expansion; + int result; + + result = history_expand (line, &expansion); + if (result) + fprintf (stderr, "%s\n", expansion); + + if (result < 0 || result == 2) + @{ + free (expansion); + continue; + @} + + add_history (expansion); + strncpy (line, expansion, sizeof (line) - 1); + free (expansion); + @} + + if (strcmp (line, "quit") == 0) + done = 1; + else if (strcmp (line, "save") == 0) + write_history ("history_file"); + else if (strcmp (line, "read") == 0) + read_history ("history_file"); + else if (strcmp (line, "list") == 0) + @{ + register HIST_ENTRY **the_list; + register int i; + + the_list = history_list (); + if (the_list) + for (i = 0; the_list[i]; i++) + printf ("%d: %s\n", i + history_base, the_list[i]->line); + @} + else if (strncmp (line, "delete", 6) == 0) + @{ + int which; + if ((sscanf (line + 6, "%d", &which)) == 1) + @{ + HIST_ENTRY *entry = remove_history (which); + if (!entry) + fprintf (stderr, "No such entry %d\n", which); + else + @{ + free (entry->line); + free (entry); + @} + @} + else + @{ + fprintf (stderr, "non-numeric arg given to `delete'\n"); + @} + @} + @} +@} +@end smallexample diff --git a/lib/readline/doc/hsuser.texi b/lib/readline/doc/hsuser.texi index 87b35417..c94eae8a 100644 --- a/lib/readline/doc/hsuser.texi +++ b/lib/readline/doc/hsuser.texi @@ -1,7 +1,7 @@ @ignore This file documents the user interface to the GNU History library. -Copyright (C) 1988-2007 Free Software Foundation, Inc. +Copyright (C) 1988--2010 Free Software Foundation, Inc. Authored by Brian Fox and Chet Ramey. Permission is granted to make and distribute verbatim copies of this manual @@ -299,6 +299,8 @@ writing the history file. An event designator is a reference to a command line entry in the history list. +Unless the reference is absolute, events are relative to the current +position in the history list. @cindex history events @table @asis @@ -324,10 +326,15 @@ Refer to the command @var{n} lines back. Refer to the previous command. This is a synonym for @samp{!-1}. @item @code{!@var{string}} -Refer to the most recent command starting with @var{string}. +Refer to the most recent command +preceding the current position in the history list +starting with @var{string}. @item @code{!?@var{string}[?]} -Refer to the most recent command containing @var{string}. The trailing +Refer to the most recent command +preceding the current position in the history list +containing @var{string}. +The trailing @samp{?} may be omitted if the @var{string} is followed immediately by a newline. diff --git a/lib/readline/doc/hsuser.texi~ b/lib/readline/doc/hsuser.texi~ new file mode 100644 index 00000000..a8cfeebc --- /dev/null +++ b/lib/readline/doc/hsuser.texi~ @@ -0,0 +1,471 @@ +@ignore +This file documents the user interface to the GNU History library. + +Copyright (C) 1988-2010 Free Software Foundation, Inc. +Authored by Brian Fox and Chet Ramey. + +Permission is granted to make and distribute verbatim copies of this manual +provided the copyright notice and this permission notice are preserved on +all copies. + +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission notice +identical to this one except for the removal of this paragraph (this +paragraph not being relevant to the printed manual). + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that the +GNU Copyright statement is available to the distributee, and provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ignore + +@node Using History Interactively +@chapter Using History Interactively + +@ifclear BashFeatures +@defcodeindex bt +@end ifclear + +@ifset BashFeatures +This chapter describes how to use the @sc{gnu} History Library +interactively, from a user's standpoint. +It should be considered a user's guide. +For information on using the @sc{gnu} History Library in other programs, +see the @sc{gnu} Readline Library Manual. +@end ifset +@ifclear BashFeatures +This chapter describes how to use the @sc{gnu} History Library interactively, +from a user's standpoint. It should be considered a user's guide. For +information on using the @sc{gnu} History Library in your own programs, +@pxref{Programming with GNU History}. +@end ifclear + +@ifset BashFeatures +@menu +* Bash History Facilities:: How Bash lets you manipulate your command + history. +* Bash History Builtins:: The Bash builtin commands that manipulate + the command history. +* History Interaction:: What it feels like using History as a user. +@end menu +@end ifset +@ifclear BashFeatures +@menu +* History Interaction:: What it feels like using History as a user. +@end menu +@end ifclear + +@ifset BashFeatures +@node Bash History Facilities +@section Bash History Facilities +@cindex command history +@cindex history list + +When the @option{-o history} option to the @code{set} builtin +is enabled (@pxref{The Set Builtin}), +the shell provides access to the @dfn{command history}, +the list of commands previously typed. +The value of the @env{HISTSIZE} shell variable is used as the +number of commands to save in a history list. +The text of the last @env{$HISTSIZE} +commands (default 500) is saved. +The shell stores each command in the history list prior to +parameter and variable expansion +but after history expansion is performed, subject to the +values of the shell variables +@env{HISTIGNORE} and @env{HISTCONTROL}. + +When the shell starts up, the history is initialized from the +file named by the @env{HISTFILE} variable (default @file{~/.bash_history}). +The file named by the value of @env{HISTFILE} is truncated, if +necessary, to contain no more than the number of lines specified by +the value of the @env{HISTFILESIZE} variable. +When an interactive shell exits, the last +@env{$HISTSIZE} lines are copied from the history list to the file +named by @env{$HISTFILE}. +If the @code{histappend} shell option is set (@pxref{Bash Builtins}), +the lines are appended to the history file, +otherwise the history file is overwritten. +If @env{HISTFILE} +is unset, or if the history file is unwritable, the history is +not saved. After saving the history, the history file is truncated +to contain no more than @env{$HISTFILESIZE} +lines. If @env{HISTFILESIZE} is not set, no truncation is performed. + +If the @env{HISTTIMEFORMAT} is set, the time stamp information +associated with each history entry is written to the history file, +marked with the history comment character. +When the history file is read, lines beginning with the history +comment character followed immediately by a digit are interpreted +as timestamps for the previous history line. + +The builtin command @code{fc} may be used to list or edit and re-execute +a portion of the history list. +The @code{history} builtin may be used to display or modify the history +list and manipulate the history file. +When using command-line editing, search commands +are available in each editing mode that provide access to the +history list (@pxref{Commands For History}). + +The shell allows control over which commands are saved on the history +list. The @env{HISTCONTROL} and @env{HISTIGNORE} +variables may be set to cause the shell to save only a subset of the +commands entered. +The @code{cmdhist} +shell option, if enabled, causes the shell to attempt to save each +line of a multi-line command in the same history entry, adding +semicolons where necessary to preserve syntactic correctness. +The @code{lithist} +shell option causes the shell to save the command with embedded newlines +instead of semicolons. +The @code{shopt} builtin is used to set these options. +@xref{Bash Builtins}, for a description of @code{shopt}. + +@node Bash History Builtins +@section Bash History Builtins +@cindex history builtins + +Bash provides two builtin commands which manipulate the +history list and history file. + +@table @code + +@item fc +@btindex fc +@example +@code{fc [-e @var{ename}] [-lnr] [@var{first}] [@var{last}]} +@code{fc -s [@var{pat}=@var{rep}] [@var{command}]} +@end example + +Fix Command. In the first form, a range of commands from @var{first} to +@var{last} is selected from the history list. Both @var{first} and +@var{last} may be specified as a string (to locate the most recent +command beginning with that string) or as a number (an index into the +history list, where a negative number is used as an offset from the +current command number). If @var{last} is not specified it is set to +@var{first}. If @var{first} is not specified it is set to the previous +command for editing and @minus{}16 for listing. If the @option{-l} flag is +given, the commands are listed on standard output. The @option{-n} flag +suppresses the command numbers when listing. The @option{-r} flag +reverses the order of the listing. Otherwise, the editor given by +@var{ename} is invoked on a file containing those commands. If +@var{ename} is not given, the value of the following variable expansion +is used: @code{$@{FCEDIT:-$@{EDITOR:-vi@}@}}. This says to use the +value of the @env{FCEDIT} variable if set, or the value of the +@env{EDITOR} variable if that is set, or @code{vi} if neither is set. +When editing is complete, the edited commands are echoed and executed. + +In the second form, @var{command} is re-executed after each instance +of @var{pat} in the selected command is replaced by @var{rep}. + +A useful alias to use with the @code{fc} command is @code{r='fc -s'}, so +that typing @samp{r cc} runs the last command beginning with @code{cc} +and typing @samp{r} re-executes the last command (@pxref{Aliases}). + +@item history +@btindex history +@example +history [@var{n}] +history -c +history -d @var{offset} +history [-anrw] [@var{filename}] +history -ps @var{arg} +@end example + +With no options, display the history list with line numbers. +Lines prefixed with a @samp{*} have been modified. +An argument of @var{n} lists only the last @var{n} lines. +If the shell variable @env{HISTTIMEFORMAT} is set and not null, +it is used as a format string for @var{strftime} to display +the time stamp associated with each displayed history entry. +No intervening blank is printed between the formatted time stamp +and the history line. + +Options, if supplied, have the following meanings: + +@table @code +@item -c +Clear the history list. This may be combined +with the other options to replace the history list completely. + +@item -d @var{offset} +Delete the history entry at position @var{offset}. +@var{offset} should be specified as it appears when the history is +displayed. + +@item -a +Append the new +history lines (history lines entered since the beginning of the +current Bash session) to the history file. + +@item -n +Append the history lines not already read from the history file +to the current history list. These are lines appended to the history +file since the beginning of the current Bash session. + +@item -r +Read the current history file and append its contents to +the history list. + +@item -w +Write out the current history to the history file. + +@item -p +Perform history substitution on the @var{arg}s and display the result +on the standard output, without storing the results in the history list. + +@item -s +The @var{arg}s are added to the end of +the history list as a single entry. + +@end table + +When any of the @option{-w}, @option{-r}, @option{-a}, or @option{-n} options is +used, if @var{filename} +is given, then it is used as the history file. If not, then +the value of the @env{HISTFILE} variable is used. + +@end table +@end ifset + +@node History Interaction +@section History Expansion +@cindex history expansion + +The History library provides a history expansion feature that is similar +to the history expansion provided by @code{csh}. This section +describes the syntax used to manipulate the history information. + +History expansions introduce words from the history list into +the input stream, making it easy to repeat commands, insert the +arguments to a previous command into the current input line, or +fix errors in previous commands quickly. + +History expansion takes place in two parts. The first is to determine +which line from the history list should be used during substitution. +The second is to select portions of that line for inclusion into the +current one. The line selected from the history is called the +@dfn{event}, and the portions of that line that are acted upon are +called @dfn{words}. Various @dfn{modifiers} are available to manipulate +the selected words. The line is broken into words in the same fashion +that Bash does, so that several words +surrounded by quotes are considered one word. +History expansions are introduced by the appearance of the +history expansion character, which is @samp{!} by default. +@ifset BashFeatures +Only @samp{\} and @samp{'} may be used to escape the history expansion +character. +@end ifset + +@ifset BashFeatures +Several shell options settable with the @code{shopt} +builtin (@pxref{Bash Builtins}) may be used to tailor +the behavior of history expansion. If the +@code{histverify} shell option is enabled, and Readline +is being used, history substitutions are not immediately passed to +the shell parser. +Instead, the expanded line is reloaded into the Readline +editing buffer for further modification. +If Readline is being used, and the @code{histreedit} +shell option is enabled, a failed history expansion will be +reloaded into the Readline editing buffer for correction. +The @option{-p} option to the @code{history} builtin command +may be used to see what a history expansion will do before using it. +The @option{-s} option to the @code{history} builtin may be used to +add commands to the end of the history list without actually executing +them, so that they are available for subsequent recall. +This is most useful in conjunction with Readline. + +The shell allows control of the various characters used by the +history expansion mechanism with the @code{histchars} variable, +as explained above (@pxref{Bash Variables}). The shell uses +the history comment character to mark history timestamps when +writing the history file. +@end ifset + +@menu +* Event Designators:: How to specify which history line to use. +* Word Designators:: Specifying which words are of interest. +* Modifiers:: Modifying the results of substitution. +@end menu + +@node Event Designators +@subsection Event Designators +@cindex event designators + +An event designator is a reference to a command line entry in the +history list. +Unless the reference is absolute, events are relative to the current +position in the history list. +@cindex history events + +@table @asis + +@item @code{!} +@ifset BashFeatures +Start a history substitution, except when followed by a space, tab, +the end of the line, @samp{=} or @samp{(} (when the +@code{extglob} shell option is enabled using the @code{shopt} builtin). +@end ifset +@ifclear BashFeatures +Start a history substitution, except when followed by a space, tab, +the end of the line, or @samp{=}. +@end ifclear + +@item @code{!@var{n}} +Refer to command line @var{n}. + +@item @code{!-@var{n}} +Refer to the command @var{n} lines back. + +@item @code{!!} +Refer to the previous command. This is a synonym for @samp{!-1}. + +@item @code{!@var{string}} +Refer to the most recent command +preceding the current position in the history list +starting with @var{string}. + +@item @code{!?@var{string}[?]} +Refer to the most recent command +preceding the current position in the history list +containing @var{string}. +The trailing +@samp{?} may be omitted if the @var{string} is followed immediately by +a newline. + +@item @code{^@var{string1}^@var{string2}^} +Quick Substitution. Repeat the last command, replacing @var{string1} +with @var{string2}. Equivalent to +@code{!!:s/@var{string1}/@var{string2}/}. + +@item @code{!#} +The entire command line typed so far. + +@end table + +@node Word Designators +@subsection Word Designators + +Word designators are used to select desired words from the event. +A @samp{:} separates the event specification from the word designator. It +may be omitted if the word designator begins with a @samp{^}, @samp{$}, +@samp{*}, @samp{-}, or @samp{%}. Words are numbered from the beginning +of the line, with the first word being denoted by 0 (zero). Words are +inserted into the current line separated by single spaces. + +@need 0.75 +For example, + +@table @code +@item !! +designates the preceding command. When you type this, the preceding +command is repeated in toto. + +@item !!:$ +designates the last argument of the preceding command. This may be +shortened to @code{!$}. + +@item !fi:2 +designates the second argument of the most recent command starting with +the letters @code{fi}. +@end table + +@need 0.75 +Here are the word designators: + +@table @code + +@item 0 (zero) +The @code{0}th word. For many applications, this is the command word. + +@item @var{n} +The @var{n}th word. + +@item ^ +The first argument; that is, word 1. + +@item $ +The last argument. + +@item % +The word matched by the most recent @samp{?@var{string}?} search. + +@item @var{x}-@var{y} +A range of words; @samp{-@var{y}} abbreviates @samp{0-@var{y}}. + +@item * +All of the words, except the @code{0}th. This is a synonym for @samp{1-$}. +It is not an error to use @samp{*} if there is just one word in the event; +the empty string is returned in that case. + +@item @var{x}* +Abbreviates @samp{@var{x}-$} + +@item @var{x}- +Abbreviates @samp{@var{x}-$} like @samp{@var{x}*}, but omits the last word. + +@end table + +If a word designator is supplied without an event specification, the +previous command is used as the event. + +@node Modifiers +@subsection Modifiers + +After the optional word designator, you can add a sequence of one or more +of the following modifiers, each preceded by a @samp{:}. + +@table @code + +@item h +Remove a trailing pathname component, leaving only the head. + +@item t +Remove all leading pathname components, leaving the tail. + +@item r +Remove a trailing suffix of the form @samp{.@var{suffix}}, leaving +the basename. + +@item e +Remove all but the trailing suffix. + +@item p +Print the new command but do not execute it. + +@ifset BashFeatures +@item q +Quote the substituted words, escaping further substitutions. + +@item x +Quote the substituted words as with @samp{q}, +but break into words at spaces, tabs, and newlines. +@end ifset + +@item s/@var{old}/@var{new}/ +Substitute @var{new} for the first occurrence of @var{old} in the +event line. Any delimiter may be used in place of @samp{/}. +The delimiter may be quoted in @var{old} and @var{new} +with a single backslash. If @samp{&} appears in @var{new}, +it is replaced by @var{old}. A single backslash will quote +the @samp{&}. The final delimiter is optional if it is the last +character on the input line. + +@item & +Repeat the previous substitution. + +@item g +@itemx a +Cause changes to be applied over the entire event line. Used in +conjunction with @samp{s}, as in @code{gs/@var{old}/@var{new}/}, +or with @samp{&}. + +@item G +Apply the following @samp{s} modifier once to each word in the event. + +@end table diff --git a/lib/readline/doc/readline.3 b/lib/readline/doc/readline.3 index d729fd1f..e83e3906 100644 --- a/lib/readline/doc/readline.3 +++ b/lib/readline/doc/readline.3 @@ -8,7 +8,7 @@ .\" .\" Last Change: Thu Apr 22 18:59:21 EDT 2010 .\" -.TH READLINE 3 "2010 April 22" "GNU Readline 6.1" +.TH READLINE 3 "2010 April 22" "GNU Readline 6.2" .\" .\" File Name macro. This used to be `.PN', for Path Name, .\" but Sun doesn't seem to like that very much. diff --git a/lib/readline/doc/readline.3~ b/lib/readline/doc/readline.3~ index ebc70d68..d729fd1f 100644 --- a/lib/readline/doc/readline.3~ +++ b/lib/readline/doc/readline.3~ @@ -8,7 +8,7 @@ .\" .\" Last Change: Thu Apr 22 18:59:21 EDT 2010 .\" -.TH READLINE 3 "2009 April 22" "GNU Readline 6.1" +.TH READLINE 3 "2010 April 22" "GNU Readline 6.1" .\" .\" File Name macro. This used to be `.PN', for Path Name, .\" but Sun doesn't seem to like that very much. diff --git a/lib/readline/doc/rltech.texi b/lib/readline/doc/rltech.texi index 454eaf03..6310cf15 100644 --- a/lib/readline/doc/rltech.texi +++ b/lib/readline/doc/rltech.texi @@ -7,7 +7,7 @@ This document describes the GNU Readline Library, a utility for aiding in the consistency of user interface across discrete programs that need to provide a command line interface. -Copyright (C) 1988-2007 Free Software Foundation, Inc. +Copyright (C) 1988--2010 Free Software Foundation, Inc. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice diff --git a/lib/readline/doc/rluserman.texi b/lib/readline/doc/rluserman.texi index 49d9a2c8..3856cd53 100644 --- a/lib/readline/doc/rluserman.texi +++ b/lib/readline/doc/rluserman.texi @@ -12,7 +12,7 @@ This manual describes the end user interface of the GNU Readline Library consistency of user interface across discrete programs which provide a command line interface. -Copyright @copyright{} 1988--2009 Free Software Foundation, Inc. +Copyright @copyright{} 1988--2010 Free Software Foundation, Inc. Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice diff --git a/lib/readline/doc/rluserman.texi~ b/lib/readline/doc/rluserman.texi~ new file mode 100644 index 00000000..49d9a2c8 --- /dev/null +++ b/lib/readline/doc/rluserman.texi~ @@ -0,0 +1,83 @@ +\input texinfo @c -*-texinfo-*- +@comment %**start of header (This is for running Texinfo on a region.) +@setfilename rluserman.info +@settitle GNU Readline Library +@comment %**end of header (This is for running Texinfo on a region.) + +@include version.texi + +@copying +This manual describes the end user interface of the GNU Readline Library +(version @value{VERSION}, @value{UPDATED}), a library which aids in the +consistency of user interface across discrete programs which provide +a command line interface. + +Copyright @copyright{} 1988--2009 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, with the Front-Cover texts being ``A GNU Manual'', +and with the Back-Cover Texts as in (a) below. A copy of the license is +included in the section entitled ``GNU Free Documentation License''. + +(a) The FSF's Back-Cover Text is: You are free to copy and modify +this GNU manual. Buying copies from GNU Press supports the FSF in +developing GNU and promoting software freedom.'' + +@end quotation +@end copying + +@dircategory Libraries +@direntry +* RLuserman: (rluserman). The GNU readline library User's Manual. +@end direntry + +@titlepage +@title GNU Readline Library User Interface +@subtitle Edition @value{EDITION}, for @code{Readline Library} Version @value{VERSION}. +@subtitle @value{UPDATED-MONTH} +@author Chet Ramey, Case Western Reserve University +@author Brian Fox, Free Software Foundation + +@page +@vskip 0pt plus 1filll +@insertcopying + +@sp 1 +Published by the Free Software Foundation @* +59 Temple Place, Suite 330, @* +Boston, MA 02111-1307 @* +USA @* + +@end titlepage + +@contents + +@ifnottex +@node Top +@top GNU Readline Library + +This document describes the end user interface of the GNU Readline Library, +a utility which aids in the consistency of user interface across discrete +programs which provide a command line interface. + +@menu +* Command Line Editing:: GNU Readline User's Manual. +* GNU Free Documentation License:: License for copying this manual. +@end menu +@end ifnottex + +@include rluser.texi + +@node GNU Free Documentation License +@appendix GNU Free Documentation License + +@include fdl.texi + +@bye diff --git a/lib/readline/doc/version.texi b/lib/readline/doc/version.texi index 97eeed9f..f2577675 100644 --- a/lib/readline/doc/version.texi +++ b/lib/readline/doc/version.texi @@ -2,9 +2,9 @@ Copyright (C) 1988-2010 Free Software Foundation, Inc. @end ignore -@set EDITION 6.1 -@set VERSION 6.1 -@set UPDATED May 292010 -@set UPDATED-MONTH May 2010 +@set EDITION 6.2 +@set VERSION 6.2 +@set UPDATED August 12 2010 +@set UPDATED-MONTH August 2010 -@set LASTCHANGE Sat May 29 17:14:10 EDT 2010 +@set LASTCHANGE Thu Aug 12 22:24:28 EDT 2010 diff --git a/lib/readline/doc/version.texi~ b/lib/readline/doc/version.texi~ index fb381d71..59ea6cdc 100644 --- a/lib/readline/doc/version.texi~ +++ b/lib/readline/doc/version.texi~ @@ -2,9 +2,9 @@ Copyright (C) 1988-2010 Free Software Foundation, Inc. @end ignore -@set EDITION 6.1 -@set VERSION 6.1 -@set UPDATED April 22 2010 -@set UPDATED-MONTH April 2010 +@set EDITION 6.2 +@set VERSION 6.2 +@set UPDATED August 3 2010 +@set UPDATED-MONTH August 2010 -@set LASTCHANGE Thu Apr 22 18:59:44 EDT 2010 +@set LASTCHANGE Tue Aug 3 15:30:05 EDT 2010 diff --git a/lib/readline/rltty.c b/lib/readline/rltty.c index 0dd5d104..d237b1c0 100644 --- a/lib/readline/rltty.c +++ b/lib/readline/rltty.c @@ -604,7 +604,7 @@ rl_prep_terminal (meta_flag) /* Try to keep this function from being INTerrupted. */ _rl_block_sigint (); - tty = fileno (rl_instream); + tty = rl_instream ? fileno (rl_instream) : fileno (stdin); if (get_tty_settings (tty, &tio) < 0) { @@ -678,7 +678,7 @@ rl_deprep_terminal () /* Try to keep this function from being interrupted. */ _rl_block_sigint (); - tty = fileno (rl_instream); + tty = rl_instream ? fileno (rl_instream) : fileno (stdout); if (_rl_enable_keypad) _rl_control_keypad (0); diff --git a/lib/readline/rltty.c~ b/lib/readline/rltty.c~ new file mode 100644 index 00000000..0dd5d104 --- /dev/null +++ b/lib/readline/rltty.c~ @@ -0,0 +1,975 @@ +/* rltty.c -- functions to prepare and restore the terminal for readline's + use. */ + +/* Copyright (C) 1992-2005 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Readline is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#include "rldefs.h" + +#if defined (GWINSZ_IN_SYS_IOCTL) +# include <sys/ioctl.h> +#endif /* GWINSZ_IN_SYS_IOCTL */ + +#include "rltty.h" +#include "readline.h" +#include "rlprivate.h" + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +rl_vintfunc_t *rl_prep_term_function = rl_prep_terminal; +rl_voidfunc_t *rl_deprep_term_function = rl_deprep_terminal; + +static void set_winsize PARAMS((int)); + +/* **************************************************************** */ +/* */ +/* Saving and Restoring the TTY */ +/* */ +/* **************************************************************** */ + +/* Non-zero means that the terminal is in a prepped state. */ +static int terminal_prepped; + +static _RL_TTY_CHARS _rl_tty_chars, _rl_last_tty_chars; + +/* If non-zero, means that this process has called tcflow(fd, TCOOFF) + and output is suspended. */ +#if defined (__ksr1__) +static int ksrflow; +#endif + +/* Dummy call to force a backgrounded readline to stop before it tries + to get the tty settings. */ +static void +set_winsize (tty) + int tty; +{ +#if defined (TIOCGWINSZ) + struct winsize w; + + if (ioctl (tty, TIOCGWINSZ, &w) == 0) + (void) ioctl (tty, TIOCSWINSZ, &w); +#endif /* TIOCGWINSZ */ +} + +#if defined (NO_TTY_DRIVER) +/* Nothing */ +#elif defined (NEW_TTY_DRIVER) + +/* Values for the `flags' field of a struct bsdtty. This tells which + elements of the struct bsdtty have been fetched from the system and + are valid. */ +#define SGTTY_SET 0x01 +#define LFLAG_SET 0x02 +#define TCHARS_SET 0x04 +#define LTCHARS_SET 0x08 + +struct bsdtty { + struct sgttyb sgttyb; /* Basic BSD tty driver information. */ + int lflag; /* Local mode flags, like LPASS8. */ +#if defined (TIOCGETC) + struct tchars tchars; /* Terminal special characters, including ^S and ^Q. */ +#endif +#if defined (TIOCGLTC) + struct ltchars ltchars; /* 4.2 BSD editing characters */ +#endif + int flags; /* Bitmap saying which parts of the struct are valid. */ +}; + +#define TIOTYPE struct bsdtty + +static TIOTYPE otio; + +static void save_tty_chars PARAMS((TIOTYPE *)); +static int _get_tty_settings PARAMS((int, TIOTYPE *)); +static int get_tty_settings PARAMS((int, TIOTYPE *)); +static int _set_tty_settings PARAMS((int, TIOTYPE *)); +static int set_tty_settings PARAMS((int, TIOTYPE *)); + +static void prepare_terminal_settings PARAMS((int, TIOTYPE, TIOTYPE *)); + +static void set_special_char PARAMS((Keymap, TIOTYPE *, int, rl_command_func_t)); + +static void +save_tty_chars (tiop) + TIOTYPE *tiop; +{ + _rl_last_tty_chars = _rl_tty_chars; + + if (tiop->flags & SGTTY_SET) + { + _rl_tty_chars.t_erase = tiop->sgttyb.sg_erase; + _rl_tty_chars.t_kill = tiop->sgttyb.sg_kill; + } + + if (tiop->flags & TCHARS_SET) + { + _rl_intr_char = _rl_tty_chars.t_intr = tiop->tchars.t_intrc; + _rl_quit_char = _rl_tty_chars.t_quit = tiop->tchars.t_quitc; + + _rl_tty_chars.t_start = tiop->tchars.t_startc; + _rl_tty_chars.t_stop = tiop->tchars.t_stopc; + _rl_tty_chars.t_eof = tiop->tchars.t_eofc; + _rl_tty_chars.t_eol = '\n'; + _rl_tty_chars.t_eol2 = tiop->tchars.t_brkc; + } + + if (tiop->flags & LTCHARS_SET) + { + _rl_susp_char = _rl_tty_chars.t_susp = tiop->ltchars.t_suspc; + + _rl_tty_chars.t_dsusp = tiop->ltchars.t_dsuspc; + _rl_tty_chars.t_reprint = tiop->ltchars.t_rprntc; + _rl_tty_chars.t_flush = tiop->ltchars.t_flushc; + _rl_tty_chars.t_werase = tiop->ltchars.t_werasc; + _rl_tty_chars.t_lnext = tiop->ltchars.t_lnextc; + } + + _rl_tty_chars.t_status = -1; +} + +static int +get_tty_settings (tty, tiop) + int tty; + TIOTYPE *tiop; +{ + set_winsize (tty); + + tiop->flags = tiop->lflag = 0; + + errno = 0; + if (ioctl (tty, TIOCGETP, &(tiop->sgttyb)) < 0) + return -1; + tiop->flags |= SGTTY_SET; + +#if defined (TIOCLGET) + if (ioctl (tty, TIOCLGET, &(tiop->lflag)) == 0) + tiop->flags |= LFLAG_SET; +#endif + +#if defined (TIOCGETC) + if (ioctl (tty, TIOCGETC, &(tiop->tchars)) == 0) + tiop->flags |= TCHARS_SET; +#endif + +#if defined (TIOCGLTC) + if (ioctl (tty, TIOCGLTC, &(tiop->ltchars)) == 0) + tiop->flags |= LTCHARS_SET; +#endif + + return 0; +} + +static int +set_tty_settings (tty, tiop) + int tty; + TIOTYPE *tiop; +{ + if (tiop->flags & SGTTY_SET) + { + ioctl (tty, TIOCSETN, &(tiop->sgttyb)); + tiop->flags &= ~SGTTY_SET; + } + _rl_echoing_p = 1; + +#if defined (TIOCLSET) + if (tiop->flags & LFLAG_SET) + { + ioctl (tty, TIOCLSET, &(tiop->lflag)); + tiop->flags &= ~LFLAG_SET; + } +#endif + +#if defined (TIOCSETC) + if (tiop->flags & TCHARS_SET) + { + ioctl (tty, TIOCSETC, &(tiop->tchars)); + tiop->flags &= ~TCHARS_SET; + } +#endif + +#if defined (TIOCSLTC) + if (tiop->flags & LTCHARS_SET) + { + ioctl (tty, TIOCSLTC, &(tiop->ltchars)); + tiop->flags &= ~LTCHARS_SET; + } +#endif + + return 0; +} + +static void +prepare_terminal_settings (meta_flag, oldtio, tiop) + int meta_flag; + TIOTYPE oldtio, *tiop; +{ + _rl_echoing_p = (oldtio.sgttyb.sg_flags & ECHO); + _rl_echoctl = (oldtio.sgttyb.sg_flags & ECHOCTL); + + /* Copy the original settings to the structure we're going to use for + our settings. */ + tiop->sgttyb = oldtio.sgttyb; + tiop->lflag = oldtio.lflag; +#if defined (TIOCGETC) + tiop->tchars = oldtio.tchars; +#endif +#if defined (TIOCGLTC) + tiop->ltchars = oldtio.ltchars; +#endif + tiop->flags = oldtio.flags; + + /* First, the basic settings to put us into character-at-a-time, no-echo + input mode. */ + tiop->sgttyb.sg_flags &= ~(ECHO | CRMOD); + tiop->sgttyb.sg_flags |= CBREAK; + + /* If this terminal doesn't care how the 8th bit is used, then we can + use it for the meta-key. If only one of even or odd parity is + specified, then the terminal is using parity, and we cannot. */ +#if !defined (ANYP) +# define ANYP (EVENP | ODDP) +#endif + if (((oldtio.sgttyb.sg_flags & ANYP) == ANYP) || + ((oldtio.sgttyb.sg_flags & ANYP) == 0)) + { + tiop->sgttyb.sg_flags |= ANYP; + + /* Hack on local mode flags if we can. */ +#if defined (TIOCLGET) +# if defined (LPASS8) + tiop->lflag |= LPASS8; +# endif /* LPASS8 */ +#endif /* TIOCLGET */ + } + +#if defined (TIOCGETC) +# if defined (USE_XON_XOFF) + /* Get rid of terminal output start and stop characters. */ + tiop->tchars.t_stopc = -1; /* C-s */ + tiop->tchars.t_startc = -1; /* C-q */ + + /* If there is an XON character, bind it to restart the output. */ + if (oldtio.tchars.t_startc != -1) + rl_bind_key (oldtio.tchars.t_startc, rl_restart_output); +# endif /* USE_XON_XOFF */ + + /* If there is an EOF char, bind _rl_eof_char to it. */ + if (oldtio.tchars.t_eofc != -1) + _rl_eof_char = oldtio.tchars.t_eofc; + +# if defined (NO_KILL_INTR) + /* Get rid of terminal-generated SIGQUIT and SIGINT. */ + tiop->tchars.t_quitc = -1; /* C-\ */ + tiop->tchars.t_intrc = -1; /* C-c */ +# endif /* NO_KILL_INTR */ +#endif /* TIOCGETC */ + +#if defined (TIOCGLTC) + /* Make the interrupt keys go away. Just enough to make people happy. */ + tiop->ltchars.t_dsuspc = -1; /* C-y */ + tiop->ltchars.t_lnextc = -1; /* C-v */ +#endif /* TIOCGLTC */ +} + +#else /* !defined (NEW_TTY_DRIVER) */ + +#if !defined (VMIN) +# define VMIN VEOF +#endif + +#if !defined (VTIME) +# define VTIME VEOL +#endif + +#if defined (TERMIOS_TTY_DRIVER) +# define TIOTYPE struct termios +# define DRAIN_OUTPUT(fd) tcdrain (fd) +# define GETATTR(tty, tiop) (tcgetattr (tty, tiop)) +# ifdef M_UNIX +# define SETATTR(tty, tiop) (tcsetattr (tty, TCSANOW, tiop)) +# else +# define SETATTR(tty, tiop) (tcsetattr (tty, TCSADRAIN, tiop)) +# endif /* !M_UNIX */ +#else +# define TIOTYPE struct termio +# define DRAIN_OUTPUT(fd) +# define GETATTR(tty, tiop) (ioctl (tty, TCGETA, tiop)) +# define SETATTR(tty, tiop) (ioctl (tty, TCSETAW, tiop)) +#endif /* !TERMIOS_TTY_DRIVER */ + +static TIOTYPE otio; + +static void save_tty_chars PARAMS((TIOTYPE *)); +static int _get_tty_settings PARAMS((int, TIOTYPE *)); +static int get_tty_settings PARAMS((int, TIOTYPE *)); +static int _set_tty_settings PARAMS((int, TIOTYPE *)); +static int set_tty_settings PARAMS((int, TIOTYPE *)); + +static void prepare_terminal_settings PARAMS((int, TIOTYPE, TIOTYPE *)); + +static void set_special_char PARAMS((Keymap, TIOTYPE *, int, rl_command_func_t)); +static void _rl_bind_tty_special_chars PARAMS((Keymap, TIOTYPE)); + +#if defined (FLUSHO) +# define OUTPUT_BEING_FLUSHED(tp) (tp->c_lflag & FLUSHO) +#else +# define OUTPUT_BEING_FLUSHED(tp) 0 +#endif + +static void +save_tty_chars (tiop) + TIOTYPE *tiop; +{ + _rl_last_tty_chars = _rl_tty_chars; + + _rl_tty_chars.t_eof = tiop->c_cc[VEOF]; + _rl_tty_chars.t_eol = tiop->c_cc[VEOL]; +#ifdef VEOL2 + _rl_tty_chars.t_eol2 = tiop->c_cc[VEOL2]; +#endif + _rl_tty_chars.t_erase = tiop->c_cc[VERASE]; +#ifdef VWERASE + _rl_tty_chars.t_werase = tiop->c_cc[VWERASE]; +#endif + _rl_tty_chars.t_kill = tiop->c_cc[VKILL]; +#ifdef VREPRINT + _rl_tty_chars.t_reprint = tiop->c_cc[VREPRINT]; +#endif + _rl_intr_char = _rl_tty_chars.t_intr = tiop->c_cc[VINTR]; + _rl_quit_char = _rl_tty_chars.t_quit = tiop->c_cc[VQUIT]; +#ifdef VSUSP + _rl_susp_char = _rl_tty_chars.t_susp = tiop->c_cc[VSUSP]; +#endif +#ifdef VDSUSP + _rl_tty_chars.t_dsusp = tiop->c_cc[VDSUSP]; +#endif +#ifdef VSTART + _rl_tty_chars.t_start = tiop->c_cc[VSTART]; +#endif +#ifdef VSTOP + _rl_tty_chars.t_stop = tiop->c_cc[VSTOP]; +#endif +#ifdef VLNEXT + _rl_tty_chars.t_lnext = tiop->c_cc[VLNEXT]; +#endif +#ifdef VDISCARD + _rl_tty_chars.t_flush = tiop->c_cc[VDISCARD]; +#endif +#ifdef VSTATUS + _rl_tty_chars.t_status = tiop->c_cc[VSTATUS]; +#endif +} + +#if defined (_AIX) || defined (_AIX41) +/* Currently this is only used on AIX */ +static void +rltty_warning (msg) + char *msg; +{ + _rl_errmsg ("warning: %s", msg); +} +#endif + +#if defined (_AIX) +void +setopost(tp) +TIOTYPE *tp; +{ + if ((tp->c_oflag & OPOST) == 0) + { + _rl_errmsg ("warning: turning on OPOST for terminal\r"); + tp->c_oflag |= OPOST|ONLCR; + } +} +#endif + +static int +_get_tty_settings (tty, tiop) + int tty; + TIOTYPE *tiop; +{ + int ioctl_ret; + + while (1) + { + ioctl_ret = GETATTR (tty, tiop); + if (ioctl_ret < 0) + { + if (errno != EINTR) + return -1; + else + continue; + } + if (OUTPUT_BEING_FLUSHED (tiop)) + { +#if defined (FLUSHO) + _rl_errmsg ("warning: turning off output flushing"); + tiop->c_lflag &= ~FLUSHO; + break; +#else + continue; +#endif + } + break; + } + + return 0; +} + +static int +get_tty_settings (tty, tiop) + int tty; + TIOTYPE *tiop; +{ + set_winsize (tty); + + errno = 0; + if (_get_tty_settings (tty, tiop) < 0) + return -1; + +#if defined (_AIX) + setopost(tiop); +#endif + + return 0; +} + +static int +_set_tty_settings (tty, tiop) + int tty; + TIOTYPE *tiop; +{ + while (SETATTR (tty, tiop) < 0) + { + if (errno != EINTR) + return -1; + errno = 0; + } + return 0; +} + +static int +set_tty_settings (tty, tiop) + int tty; + TIOTYPE *tiop; +{ + if (_set_tty_settings (tty, tiop) < 0) + return -1; + +#if 0 + +#if defined (TERMIOS_TTY_DRIVER) +# if defined (__ksr1__) + if (ksrflow) + { + ksrflow = 0; + tcflow (tty, TCOON); + } +# else /* !ksr1 */ + tcflow (tty, TCOON); /* Simulate a ^Q. */ +# endif /* !ksr1 */ +#else + ioctl (tty, TCXONC, 1); /* Simulate a ^Q. */ +#endif /* !TERMIOS_TTY_DRIVER */ + +#endif /* 0 */ + + return 0; +} + +static void +prepare_terminal_settings (meta_flag, oldtio, tiop) + int meta_flag; + TIOTYPE oldtio, *tiop; +{ + _rl_echoing_p = (oldtio.c_lflag & ECHO); +#if defined (ECHOCTL) + _rl_echoctl = (oldtio.c_lflag & ECHOCTL); +#endif + + tiop->c_lflag &= ~(ICANON | ECHO); + + if ((unsigned char) oldtio.c_cc[VEOF] != (unsigned char) _POSIX_VDISABLE) + _rl_eof_char = oldtio.c_cc[VEOF]; + +#if defined (USE_XON_XOFF) +#if defined (IXANY) + tiop->c_iflag &= ~(IXON | IXOFF | IXANY); +#else + /* `strict' Posix systems do not define IXANY. */ + tiop->c_iflag &= ~(IXON | IXOFF); +#endif /* IXANY */ +#endif /* USE_XON_XOFF */ + + /* Only turn this off if we are using all 8 bits. */ + if (((tiop->c_cflag & CSIZE) == CS8) || meta_flag) + tiop->c_iflag &= ~(ISTRIP | INPCK); + + /* Make sure we differentiate between CR and NL on input. */ + tiop->c_iflag &= ~(ICRNL | INLCR); + +#if !defined (HANDLE_SIGNALS) + tiop->c_lflag &= ~ISIG; +#else + tiop->c_lflag |= ISIG; +#endif + + tiop->c_cc[VMIN] = 1; + tiop->c_cc[VTIME] = 0; + +#if defined (FLUSHO) + if (OUTPUT_BEING_FLUSHED (tiop)) + { + tiop->c_lflag &= ~FLUSHO; + oldtio.c_lflag &= ~FLUSHO; + } +#endif + + /* Turn off characters that we need on Posix systems with job control, + just to be sure. This includes ^Y and ^V. This should not really + be necessary. */ +#if defined (TERMIOS_TTY_DRIVER) && defined (_POSIX_VDISABLE) + +#if defined (VLNEXT) + tiop->c_cc[VLNEXT] = _POSIX_VDISABLE; +#endif + +#if defined (VDSUSP) + tiop->c_cc[VDSUSP] = _POSIX_VDISABLE; +#endif + +#endif /* TERMIOS_TTY_DRIVER && _POSIX_VDISABLE */ +} +#endif /* !NEW_TTY_DRIVER */ + +/* Put the terminal in CBREAK mode so that we can detect key presses. */ +#if defined (NO_TTY_DRIVER) +void +rl_prep_terminal (meta_flag) + int meta_flag; +{ + _rl_echoing_p = 1; +} + +void +rl_deprep_terminal () +{ +} + +#else /* ! NO_TTY_DRIVER */ +void +rl_prep_terminal (meta_flag) + int meta_flag; +{ + int tty; + TIOTYPE tio; + + if (terminal_prepped) + return; + + /* Try to keep this function from being INTerrupted. */ + _rl_block_sigint (); + + tty = fileno (rl_instream); + + if (get_tty_settings (tty, &tio) < 0) + { +#if defined (ENOTSUP) + /* MacOS X and Linux, at least, lie about the value of errno if + tcgetattr fails. */ + if (errno == ENOTTY || errno == EINVAL || errno == ENOTSUP) +#else + if (errno == ENOTTY || errno == EINVAL) +#endif + _rl_echoing_p = 1; /* XXX */ + + _rl_release_sigint (); + return; + } + + otio = tio; + + if (_rl_bind_stty_chars) + { +#if defined (VI_MODE) + /* If editing in vi mode, make sure we restore the bindings in the + insertion keymap no matter what keymap we ended up in. */ + if (rl_editing_mode == vi_mode) + rl_tty_unset_default_bindings (vi_insertion_keymap); + else +#endif + rl_tty_unset_default_bindings (_rl_keymap); + } + save_tty_chars (&otio); + RL_SETSTATE(RL_STATE_TTYCSAVED); + if (_rl_bind_stty_chars) + { +#if defined (VI_MODE) + /* If editing in vi mode, make sure we set the bindings in the + insertion keymap no matter what keymap we ended up in. */ + if (rl_editing_mode == vi_mode) + _rl_bind_tty_special_chars (vi_insertion_keymap, tio); + else +#endif + _rl_bind_tty_special_chars (_rl_keymap, tio); + } + + prepare_terminal_settings (meta_flag, otio, &tio); + + if (set_tty_settings (tty, &tio) < 0) + { + _rl_release_sigint (); + return; + } + + if (_rl_enable_keypad) + _rl_control_keypad (1); + + fflush (rl_outstream); + terminal_prepped = 1; + RL_SETSTATE(RL_STATE_TERMPREPPED); + + _rl_release_sigint (); +} + +/* Restore the terminal's normal settings and modes. */ +void +rl_deprep_terminal () +{ + int tty; + + if (!terminal_prepped) + return; + + /* Try to keep this function from being interrupted. */ + _rl_block_sigint (); + + tty = fileno (rl_instream); + + if (_rl_enable_keypad) + _rl_control_keypad (0); + + fflush (rl_outstream); + + if (set_tty_settings (tty, &otio) < 0) + { + _rl_release_sigint (); + return; + } + + terminal_prepped = 0; + RL_UNSETSTATE(RL_STATE_TERMPREPPED); + + _rl_release_sigint (); +} +#endif /* !NO_TTY_DRIVER */ + +/* **************************************************************** */ +/* */ +/* Bogus Flow Control */ +/* */ +/* **************************************************************** */ + +int +rl_restart_output (count, key) + int count, key; +{ +#if defined (__MINGW32__) + return 0; +#else /* !__MING32__ */ + + int fildes = fileno (rl_outstream); +#if defined (TIOCSTART) +#if defined (apollo) + ioctl (&fildes, TIOCSTART, 0); +#else + ioctl (fildes, TIOCSTART, 0); +#endif /* apollo */ + +#else /* !TIOCSTART */ +# if defined (TERMIOS_TTY_DRIVER) +# if defined (__ksr1__) + if (ksrflow) + { + ksrflow = 0; + tcflow (fildes, TCOON); + } +# else /* !ksr1 */ + tcflow (fildes, TCOON); /* Simulate a ^Q. */ +# endif /* !ksr1 */ +# else /* !TERMIOS_TTY_DRIVER */ +# if defined (TCXONC) + ioctl (fildes, TCXONC, TCOON); +# endif /* TCXONC */ +# endif /* !TERMIOS_TTY_DRIVER */ +#endif /* !TIOCSTART */ + + return 0; +#endif /* !__MINGW32__ */ +} + +int +rl_stop_output (count, key) + int count, key; +{ +#if defined (__MINGW32__) + return 0; +#else + + int fildes = fileno (rl_instream); + +#if defined (TIOCSTOP) +# if defined (apollo) + ioctl (&fildes, TIOCSTOP, 0); +# else + ioctl (fildes, TIOCSTOP, 0); +# endif /* apollo */ +#else /* !TIOCSTOP */ +# if defined (TERMIOS_TTY_DRIVER) +# if defined (__ksr1__) + ksrflow = 1; +# endif /* ksr1 */ + tcflow (fildes, TCOOFF); +# else +# if defined (TCXONC) + ioctl (fildes, TCXONC, TCOON); +# endif /* TCXONC */ +# endif /* !TERMIOS_TTY_DRIVER */ +#endif /* !TIOCSTOP */ + + return 0; +#endif /* !__MINGW32__ */ +} + +/* **************************************************************** */ +/* */ +/* Default Key Bindings */ +/* */ +/* **************************************************************** */ + +#if !defined (NO_TTY_DRIVER) +#define SET_SPECIAL(sc, func) set_special_char(kmap, &ttybuff, sc, func) +#endif + +#if defined (NO_TTY_DRIVER) + +#define SET_SPECIAL(sc, func) +#define RESET_SPECIAL(c) + +#elif defined (NEW_TTY_DRIVER) +static void +set_special_char (kmap, tiop, sc, func) + Keymap kmap; + TIOTYPE *tiop; + int sc; + rl_command_func_t *func; +{ + if (sc != -1 && kmap[(unsigned char)sc].type == ISFUNC) + kmap[(unsigned char)sc].function = func; +} + +#define RESET_SPECIAL(c) \ + if (c != -1 && kmap[(unsigned char)c].type == ISFUNC) \ + kmap[(unsigned char)c].function = rl_insert; + +static void +_rl_bind_tty_special_chars (kmap, ttybuff) + Keymap kmap; + TIOTYPE ttybuff; +{ + if (ttybuff.flags & SGTTY_SET) + { + SET_SPECIAL (ttybuff.sgttyb.sg_erase, rl_rubout); + SET_SPECIAL (ttybuff.sgttyb.sg_kill, rl_unix_line_discard); + } + +# if defined (TIOCGLTC) + if (ttybuff.flags & LTCHARS_SET) + { + SET_SPECIAL (ttybuff.ltchars.t_werasc, rl_unix_word_rubout); + SET_SPECIAL (ttybuff.ltchars.t_lnextc, rl_quoted_insert); + } +# endif /* TIOCGLTC */ +} + +#else /* !NEW_TTY_DRIVER */ +static void +set_special_char (kmap, tiop, sc, func) + Keymap kmap; + TIOTYPE *tiop; + int sc; + rl_command_func_t *func; +{ + unsigned char uc; + + uc = tiop->c_cc[sc]; + if (uc != (unsigned char)_POSIX_VDISABLE && kmap[uc].type == ISFUNC) + kmap[uc].function = func; +} + +/* used later */ +#define RESET_SPECIAL(uc) \ + if (uc != (unsigned char)_POSIX_VDISABLE && kmap[uc].type == ISFUNC) \ + kmap[uc].function = rl_insert; + +static void +_rl_bind_tty_special_chars (kmap, ttybuff) + Keymap kmap; + TIOTYPE ttybuff; +{ + SET_SPECIAL (VERASE, rl_rubout); + SET_SPECIAL (VKILL, rl_unix_line_discard); + +# if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER) + SET_SPECIAL (VLNEXT, rl_quoted_insert); +# endif /* VLNEXT && TERMIOS_TTY_DRIVER */ + +# if defined (VWERASE) && defined (TERMIOS_TTY_DRIVER) + SET_SPECIAL (VWERASE, rl_unix_word_rubout); +# endif /* VWERASE && TERMIOS_TTY_DRIVER */ +} + +#endif /* !NEW_TTY_DRIVER */ + +/* Set the system's default editing characters to their readline equivalents + in KMAP. Should be static, now that we have rl_tty_set_default_bindings. */ +void +rltty_set_default_bindings (kmap) + Keymap kmap; +{ +#if !defined (NO_TTY_DRIVER) + TIOTYPE ttybuff; + int tty; + + tty = fileno (rl_instream); + + if (get_tty_settings (tty, &ttybuff) == 0) + _rl_bind_tty_special_chars (kmap, ttybuff); +#endif +} + +/* New public way to set the system default editing chars to their readline + equivalents. */ +void +rl_tty_set_default_bindings (kmap) + Keymap kmap; +{ + rltty_set_default_bindings (kmap); +} + +/* Rebind all of the tty special chars that readline worries about back + to self-insert. Call this before saving the current terminal special + chars with save_tty_chars(). This only works on POSIX termios or termio + systems. */ +void +rl_tty_unset_default_bindings (kmap) + Keymap kmap; +{ + /* Don't bother before we've saved the tty special chars at least once. */ + if (RL_ISSTATE(RL_STATE_TTYCSAVED) == 0) + return; + + RESET_SPECIAL (_rl_tty_chars.t_erase); + RESET_SPECIAL (_rl_tty_chars.t_kill); + +# if defined (VLNEXT) && defined (TERMIOS_TTY_DRIVER) + RESET_SPECIAL (_rl_tty_chars.t_lnext); +# endif /* VLNEXT && TERMIOS_TTY_DRIVER */ + +# if defined (VWERASE) && defined (TERMIOS_TTY_DRIVER) + RESET_SPECIAL (_rl_tty_chars.t_werase); +# endif /* VWERASE && TERMIOS_TTY_DRIVER */ +} + +#if defined (HANDLE_SIGNALS) + +#if defined (NEW_TTY_DRIVER) || defined (NO_TTY_DRIVER) +int +_rl_disable_tty_signals () +{ + return 0; +} + +int +_rl_restore_tty_signals () +{ + return 0; +} +#else + +static TIOTYPE sigstty, nosigstty; +static int tty_sigs_disabled = 0; + +int +_rl_disable_tty_signals () +{ + if (tty_sigs_disabled) + return 0; + + if (_get_tty_settings (fileno (rl_instream), &sigstty) < 0) + return -1; + + nosigstty = sigstty; + + nosigstty.c_lflag &= ~ISIG; + nosigstty.c_iflag &= ~IXON; + + if (_set_tty_settings (fileno (rl_instream), &nosigstty) < 0) + return (_set_tty_settings (fileno (rl_instream), &sigstty)); + + tty_sigs_disabled = 1; + return 0; +} + +int +_rl_restore_tty_signals () +{ + int r; + + if (tty_sigs_disabled == 0) + return 0; + + r = _set_tty_settings (fileno (rl_instream), &sigstty); + + if (r == 0) + tty_sigs_disabled = 0; + + return r; +} +#endif /* !NEW_TTY_DRIVER */ + +#endif /* HANDLE_SIGNALS */ diff --git a/lib/readline/savestring.c b/lib/readline/savestring.c index 63f467a0..af985380 100644 --- a/lib/readline/savestring.c +++ b/lib/readline/savestring.c @@ -33,5 +33,9 @@ char * savestring (s) const char *s; { - return ((char *)strcpy ((char *)xmalloc (1 + strlen (s)), (s))); + char *ret; + + ret = (char *)xmalloc (strlen (s) + 1); + strcpy (ret, s); + return ret; } diff --git a/lib/readline/savestring.c~ b/lib/readline/savestring.c~ new file mode 100644 index 00000000..63f467a0 --- /dev/null +++ b/lib/readline/savestring.c~ @@ -0,0 +1,37 @@ +/* savestring.c - function version of savestring for backwards compatibility */ + +/* Copyright (C) 1998,2003 Free Software Foundation, Inc. + + This file is part of the GNU Readline Library (Readline), a library + for reading lines of text with interactive input and history editing. + + Readline is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Readline is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Readline. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define READLINE_LIBRARY + +#include <config.h> +#ifdef HAVE_STRING_H +# include <string.h> +#endif +#include "xmalloc.h" + +/* Backwards compatibility, now that savestring has been removed from + all `public' readline header files. */ +char * +savestring (s) + const char *s; +{ + return ((char *)strcpy ((char *)xmalloc (1 + strlen (s)), (s))); +} diff --git a/lib/sh/casemod.c b/lib/sh/casemod.c index d85549a2..3127d8c4 100644 --- a/lib/sh/casemod.c +++ b/lib/sh/casemod.c @@ -111,6 +111,13 @@ sh_modcase (string, pat, flags) mbstate_t state; #endif + if (string == 0 || *string == 0) + { + ret = (char *)xmalloc (1); + ret[0] = '\0'; + return ret; + } + #if defined (HANDLE_MULTIBYTE) memset (&state, 0, sizeof (mbstate_t)); #endif diff --git a/lib/sh/casemod.c~ b/lib/sh/casemod.c~ new file mode 100644 index 00000000..28fb3084 --- /dev/null +++ b/lib/sh/casemod.c~ @@ -0,0 +1,256 @@ +/* casemod.c -- functions to change case of strings */ + +/* Copyright (C) 2008,2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#if defined (HAVE_CONFIG_H) +# include <config.h> +#endif + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ + +#include <stdc.h> + +#include <bashansi.h> +#include <bashintl.h> +#include <bashtypes.h> + +#include <stdio.h> +#include <ctype.h> +#include <xmalloc.h> + +#include <shmbutil.h> +#include <chartypes.h> + +#include <glob/strmatch.h> + +#define _to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc)) +#define _to_wlower(wc) (iswupper (wc) ? towlower (wc) : (wc)) + +#if !defined (HANDLE_MULTIBYTE) +# define cval(s, i) ((s)[(i)]) +# define iswalnum(c) (isalnum(c)) +# define TOGGLE(x) (ISUPPER (x) ? tolower (x) : (TOUPPER (x))) +#else +# define TOGGLE(x) (iswupper (x) ? towlower (x) : (_to_wupper(x))) +#endif + +/* These must agree with the defines in externs.h */ +#define CASE_NOOP 0x0000 +#define CASE_LOWER 0x0001 +#define CASE_UPPER 0x0002 +#define CASE_CAPITALIZE 0x0004 +#define CASE_UNCAP 0x0008 +#define CASE_TOGGLE 0x0010 +#define CASE_TOGGLEALL 0x0020 +#define CASE_UPFIRST 0x0040 +#define CASE_LOWFIRST 0x0080 + +#define CASE_USEWORDS 0x1000 /* modify behavior to act on words in passed string */ + +extern char *substring __P((char *, int, int)); + +#if defined (HANDLE_MULTIBYTE) +static wchar_t +cval (s, i) + char *s; + int i; +{ + size_t tmp; + wchar_t wc; + int l; + mbstate_t mps; + + if (MB_CUR_MAX == 1) + return ((wchar_t)s[i]); + l = strlen (s); + if (i >= (l - 1)) + return ((wchar_t)s[i]); + memset (&mps, 0, sizeof (mbstate_t)); + tmp = mbrtowc (&wc, s + i, l - i, &mps); + if (MB_INVALIDCH (tmp) || MB_NULLWCH (tmp)) + return ((wchar_t)s[i]); + return wc; +} +#endif + +/* Modify the case of characters in STRING matching PAT based on the value of + FLAGS. If PAT is null, modify the case of each character */ +char * +sh_modcase (string, pat, flags) + const char *string; + char *pat; + int flags; +{ + int start, next, end; + int inword, c, nc, nop, match, usewords; + char *ret, *s; + wchar_t wc; +#if defined (HANDLE_MULTIBYTE) + wchar_t nwc; + char mb[MB_LEN_MAX+1]; + int mlen; + size_t m; + mbstate_t state; +#endif + + if (string == 0 || *string == 0) + { +#if 0 + ret = (char *)xmalloc (1); + ret[0] = '\0'; + return ret; +#else + extern char *savestring(); + return (char *)savestring (""); +#endif + } + +#if defined (HANDLE_MULTIBYTE) + memset (&state, 0, sizeof (mbstate_t)); +#endif + + start = 0; + end = strlen (string); + + ret = (char *)xmalloc (end + 1); + strcpy (ret, string); + + /* See if we are supposed to split on alphanumerics and operate on each word */ + usewords = (flags & CASE_USEWORDS); + flags &= ~CASE_USEWORDS; + + inword = 0; + while (start < end) + { + wc = cval (ret, start); + + if (iswalnum (wc) == 0) + { + inword = 0; + ADVANCE_CHAR (ret, end, start); + continue; + } + + if (pat) + { + next = start; + ADVANCE_CHAR (ret, end, next); + s = substring (ret, start, next); + match = strmatch (pat, s, FNM_EXTMATCH) != FNM_NOMATCH; + free (s); + if (match == 0) + { + start = next; + inword = 1; + continue; + } + } + + /* XXX - for now, the toggling operators work on the individual + words in the string, breaking on alphanumerics. Should I + leave the capitalization operators to do that also? */ + if (flags == CASE_CAPITALIZE) + { + if (usewords) + nop = inword ? CASE_LOWER : CASE_UPPER; + else + nop = (start > 0) ? CASE_LOWER : CASE_UPPER; + inword = 1; + } + else if (flags == CASE_UNCAP) + { + if (usewords) + nop = inword ? CASE_UPPER : CASE_LOWER; + else + nop = (start > 0) ? CASE_UPPER : CASE_LOWER; + inword = 1; + } + else if (flags == CASE_UPFIRST) + { + if (usewords) + nop = inword ? CASE_NOOP : CASE_UPPER; + else + nop = (start > 0) ? CASE_NOOP : CASE_UPPER; + inword = 1; + } + else if (flags == CASE_LOWFIRST) + { + if (usewords) + nop = inword ? CASE_NOOP : CASE_LOWER; + else + nop = (start > 0) ? CASE_NOOP : CASE_LOWER; + inword = 1; + } + else if (flags == CASE_TOGGLE) + { + nop = inword ? CASE_NOOP : CASE_TOGGLE; + inword = 1; + } + else + nop = flags; + + if (MB_CUR_MAX == 1 || isascii (wc)) + { + switch (nop) + { + default: + case CASE_NOOP: nc = wc; break; + case CASE_UPPER: nc = TOUPPER (wc); break; + case CASE_LOWER: nc = TOLOWER (wc); break; + case CASE_TOGGLEALL: + case CASE_TOGGLE: nc = TOGGLE (wc); break; + } + ret[start] = nc; + } +#if defined (HANDLE_MULTIBYTE) + else + { + m = mbrtowc (&wc, string + start, end - start, &state); + if (MB_INVALIDCH (m)) + wc = (wchar_t)string[start]; + else if (MB_NULLWCH (m)) + wc = L'\0'; + switch (nop) + { + default: + case CASE_NOOP: nwc = wc; break; + case CASE_UPPER: nwc = TOUPPER (wc); break; + case CASE_LOWER: nwc = TOLOWER (wc); break; + case CASE_TOGGLEALL: + case CASE_TOGGLE: nwc = TOGGLE (wc); break; + } + if (nwc != wc) /* just skip unchanged characters */ + { + mlen = wcrtomb (mb, nwc, &state); + if (mlen > 0) + mb[mlen] = '\0'; + /* Assume the same width */ + strncpy (ret + start, mb, mlen); + } + } +#endif + + /* This assumes that the upper and lower case versions are the same width. */ + ADVANCE_CHAR (ret, end, start); + } + + return ret; +} @@ -5875,6 +5875,8 @@ save_parser_state (ps) ps->input_line_terminator = shell_input_line_terminator; ps->eof_encountered = eof_encountered; + ps->prompt_string_pointer = prompt_string_pointer; + ps->current_command_line_count = current_command_line_count; #if defined (HISTORY) @@ -5915,6 +5917,8 @@ restore_parser_state (ps) shell_input_line_terminator = ps->input_line_terminator; eof_encountered = ps->eof_encountered; + prompt_string_pointer = ps->prompt_string_pointer; + current_command_line_count = ps->current_command_line_count; #if defined (HISTORY) @@ -2658,10 +2658,13 @@ static int time_command_acceptable () { #if defined (COMMAND_TIMING) - int c, i; + int i; if (posixly_correct && shell_compatibility_level > 41) { + /* Quick check of the rest of the line to find the next token. If it + begins with a `-', Posix says to not return `time' as the token. + This was interp 267. */ i = shell_input_line_index; while (i < shell_input_line_len && (shell_input_line[i] == ' ' || shell_input_line[i] == '\t')) i++; @@ -3552,7 +3555,7 @@ eof_error: } /* Skip whitespace */ - if MBTEST(shellblank (ch) && lex_rwlen == 0) + if MBTEST(shellblank (ch) && (tflags & LEX_HEREDELIM) == 0 && lex_rwlen == 0) { /* Add this character. */ RESIZE_MALLOCED_BUFFER (ret, retind, 1, retsize, 64); @@ -4798,6 +4801,8 @@ reserved_word_acceptable (toksym) if (last_read_token == WORD && token_before_that == COPROC) return 1; #endif + if (last_read_token == WORD && token_before_that == FUNCTION) + return 1; return 0; } } @@ -4948,6 +4953,7 @@ prompt_again () { char *temp_prompt; +itrace("prompt_again()"); if (interactive == 0 || expanding_alias ()) /* XXX */ return; @@ -4982,6 +4988,7 @@ prompt_again () FREE (current_decoded_prompt); current_decoded_prompt = temp_prompt; } +itrace("prompt_again: current_prompt_string = %s", current_prompt_string); } int @@ -5870,6 +5877,8 @@ save_parser_state (ps) ps->input_line_terminator = shell_input_line_terminator; ps->eof_encountered = eof_encountered; + ps->prompt_string_pointer = prompt_string_pointer; + ps->current_command_line_count = current_command_line_count; #if defined (HISTORY) @@ -5910,6 +5919,8 @@ restore_parser_state (ps) shell_input_line_terminator = ps->input_line_terminator; eof_encountered = ps->eof_encountered; + prompt_string_pointer = ps->prompt_string_pointer; + current_command_line_count = ps->current_command_line_count; #if defined (HISTORY) @@ -40,6 +40,7 @@ static int glob_name_is_acceptable __P((const char *)); static void ignore_globbed_names __P((char **, sh_ignore_func_t *)); +static char *split_ignorespec __P((char *, int *)); #if defined (USE_POSIX_GLOB_LIBRARY) # include <glob.h> @@ -428,6 +429,30 @@ ignore_glob_matches (names) ignore_globbed_names (names, glob_name_is_acceptable); } +static char * +split_ignorespec (s, ip) + char *s; + int *ip; +{ + char *t; + int n, i; + + if (s == 0) + return 0; + + i = *ip; + if (s[i] == 0) + return 0; + + n = skip_to_delim (s, i, ":", SD_NOJMP|SD_EXTGLOB); + t = substring (s, i, n); + + if (s[n] == ':') + n++; + *ip = n; + return t; +} + void setup_ignore_patterns (ivp) struct ignorevar *ivp; @@ -467,7 +492,11 @@ setup_ignore_patterns (ivp) numitems = maxitems = ptr = 0; +#if 0 while (colon_bit = extract_colon_unit (this_ignoreval, &ptr)) +#else + while (colon_bit = split_ignorespec (this_ignoreval, &ptr)) +#endif { if (numitems + 1 >= maxitems) { diff --git a/pathexp.c~ b/pathexp.c~ new file mode 100644 index 00000000..71f0cf31 --- /dev/null +++ b/pathexp.c~ @@ -0,0 +1,511 @@ +/* pathexp.c -- The shell interface to the globbing library. */ + +/* Copyright (C) 1995-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" +#include <stdio.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" + +#include "shell.h" +#include "pathexp.h" +#include "flags.h" + +#include "shmbutil.h" +#include "bashintl.h" + +#include <glob/strmatch.h> + +static int glob_name_is_acceptable __P((const char *)); +static void ignore_globbed_names __P((char **, sh_ignore_func_t *)); + +#if defined (USE_POSIX_GLOB_LIBRARY) +# include <glob.h> +typedef int posix_glob_errfunc_t __P((const char *, int)); +#else +# include <glob/glob.h> +#endif + +/* Control whether * matches .files in globbing. */ +int glob_dot_filenames; + +/* Control whether the extended globbing features are enabled. */ +int extended_glob = EXTGLOB_DEFAULT; + +/* Control enabling special handling of `**' */ +int glob_star = 0; + +/* Return nonzero if STRING has any unquoted special globbing chars in it. */ +int +unquoted_glob_pattern_p (string) + register char *string; +{ + register int c; + char *send; + int open; + + DECLARE_MBSTATE; + + open = 0; + send = string + strlen (string); + + while (c = *string++) + { + switch (c) + { + case '?': + case '*': + return (1); + + case '[': + open++; + continue; + + case ']': + if (open) + return (1); + continue; + + case '+': + case '@': + case '!': + if (*string == '(') /*)*/ + return (1); + continue; + + case CTLESC: + case '\\': + if (*string++ == '\0') + return (0); + } + + /* Advance one fewer byte than an entire multibyte character to + account for the auto-increment in the loop above. */ +#ifdef HANDLE_MULTIBYTE + string--; + ADVANCE_CHAR_P (string, send - string); + string++; +#else + ADVANCE_CHAR_P (string, send - string); +#endif + } + return (0); +} + +/* Return 1 if C is a character that is `special' in a POSIX ERE and needs to + be quoted to match itself. */ +static inline int +ere_char (c) + int c; +{ + switch (c) + { + case '.': + case '[': + case '\\': + case '(': + case ')': + case '*': + case '+': + case '?': + case '{': + case '|': + case '^': + case '$': + return 1; + default: + return 0; + } + return (0); +} + +int +glob_char_p (s) + const char *s; +{ + switch (*s) + { + case '*': + case '[': + case ']': + case '?': + case '\\': + return 1; + case '+': + case '@': + case '!': + if (s[1] == '(') /*(*/ + return 1; + break; + } + return 0; +} + +/* PATHNAME can contain characters prefixed by CTLESC; this indicates + that the character is to be quoted. We quote it here in the style + that the glob library recognizes. If flags includes QGLOB_CVTNULL, + we change quoted null strings (pathname[0] == CTLNUL) into empty + strings (pathname[0] == 0). If this is called after quote removal + is performed, (flags & QGLOB_CVTNULL) should be 0; if called when quote + removal has not been done (for example, before attempting to match a + pattern while executing a case statement), flags should include + QGLOB_CVTNULL. If flags includes QGLOB_FILENAME, appropriate quoting + to match a filename should be performed. */ +char * +quote_string_for_globbing (pathname, qflags) + const char *pathname; + int qflags; +{ + char *temp; + register int i, j; + + temp = (char *)xmalloc (strlen (pathname) + 1); + + if ((qflags & QGLOB_CVTNULL) && QUOTED_NULL (pathname)) + { + temp[0] = '\0'; + return temp; + } + + for (i = j = 0; pathname[i]; i++) + { + if (pathname[i] == CTLESC) + { + if ((qflags & QGLOB_FILENAME) && pathname[i+1] == '/') + continue; + if ((qflags & QGLOB_REGEXP) && ere_char (pathname[i+1]) == 0) + continue; + temp[j++] = '\\'; + i++; + if (pathname[i] == '\0') + break; + } + else if (pathname[i] == '\\') + { + temp[j++] = '\\'; + i++; + if (pathname[i] == '\0') + break; + } + temp[j++] = pathname[i]; + } + temp[j] = '\0'; + + return (temp); +} + +char * +quote_globbing_chars (string) + char *string; +{ + size_t slen; + char *temp, *s, *t, *send; + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + + temp = (char *)xmalloc (slen * 2 + 1); + for (t = temp, s = string; *s; ) + { + if (glob_char_p (s)) + *t++ = '\\'; + + /* Copy a single (possibly multibyte) character from s to t, + incrementing both. */ + COPY_CHAR_P (t, s, send); + } + *t = '\0'; + return temp; +} + +/* Call the glob library to do globbing on PATHNAME. */ +char ** +shell_glob_filename (pathname) + const char *pathname; +{ +#if defined (USE_POSIX_GLOB_LIBRARY) + register int i; + char *temp, **results; + glob_t filenames; + int glob_flags; + + temp = quote_string_for_globbing (pathname, QGLOB_FILENAME); + + filenames.gl_offs = 0; + +# if defined (GLOB_PERIOD) + glob_flags = glob_dot_filenames ? GLOB_PERIOD : 0; +# else + glob_flags = 0; +# endif /* !GLOB_PERIOD */ + + glob_flags |= (GLOB_ERR | GLOB_DOOFFS); + + i = glob (temp, glob_flags, (posix_glob_errfunc_t *)NULL, &filenames); + + free (temp); + + if (i == GLOB_NOSPACE || i == GLOB_ABORTED) + return ((char **)NULL); + else if (i == GLOB_NOMATCH) + filenames.gl_pathv = (char **)NULL; + else if (i != 0) /* other error codes not in POSIX.2 */ + filenames.gl_pathv = (char **)NULL; + + results = filenames.gl_pathv; + + if (results && ((GLOB_FAILED (results)) == 0)) + { + if (should_ignore_glob_matches ()) + ignore_glob_matches (results); + if (results && results[0]) + strvec_sort (results); + else + { + FREE (results); + results = (char **)NULL; + } + } + + return (results); + +#else /* !USE_POSIX_GLOB_LIBRARY */ + + char *temp, **results; + + noglob_dot_filenames = glob_dot_filenames == 0; + + temp = quote_string_for_globbing (pathname, QGLOB_FILENAME); + results = glob_filename (temp, glob_star ? GX_GLOBSTAR : 0); + free (temp); + + if (results && ((GLOB_FAILED (results)) == 0)) + { + if (should_ignore_glob_matches ()) + ignore_glob_matches (results); + if (results && results[0]) + strvec_sort (results); + else + { + FREE (results); + results = (char **)&glob_error_return; + } + } + + return (results); +#endif /* !USE_POSIX_GLOB_LIBRARY */ +} + +/* Stuff for GLOBIGNORE. */ + +static struct ignorevar globignore = +{ + "GLOBIGNORE", + (struct ign *)0, + 0, + (char *)0, + (sh_iv_item_func_t *)0, +}; + +/* Set up to ignore some glob matches because the value of GLOBIGNORE + has changed. If GLOBIGNORE is being unset, we also need to disable + the globbing of filenames beginning with a `.'. */ +void +setup_glob_ignore (name) + char *name; +{ + char *v; + + v = get_string_value (name); + setup_ignore_patterns (&globignore); + + if (globignore.num_ignores) + glob_dot_filenames = 1; + else if (v == 0) + glob_dot_filenames = 0; +} + +int +should_ignore_glob_matches () +{ + return globignore.num_ignores; +} + +/* Return 0 if NAME matches a pattern in the globignore.ignores list. */ +static int +glob_name_is_acceptable (name) + const char *name; +{ + struct ign *p; + int flags; + + /* . and .. are never matched */ + if (name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) + return (0); + + flags = FNM_PATHNAME | FNMATCH_EXTFLAG; + for (p = globignore.ignores; p->val; p++) + { + if (strmatch (p->val, (char *)name, flags) != FNM_NOMATCH) + return (0); + } + return (1); +} + +/* Internal function to test whether filenames in NAMES should be + ignored. NAME_FUNC is a pointer to a function to call with each + name. It returns non-zero if the name is acceptable to the particular + ignore function which called _ignore_names; zero if the name should + be removed from NAMES. */ + +static void +ignore_globbed_names (names, name_func) + char **names; + sh_ignore_func_t *name_func; +{ + char **newnames; + int n, i; + + for (i = 0; names[i]; i++) + ; + newnames = strvec_create (i + 1); + + for (n = i = 0; names[i]; i++) + { + if ((*name_func) (names[i])) + newnames[n++] = names[i]; + else + free (names[i]); + } + + newnames[n] = (char *)NULL; + + if (n == 0) + { + names[0] = (char *)NULL; + free (newnames); + return; + } + + /* Copy the acceptable names from NEWNAMES back to NAMES and set the + new array end. */ + for (n = 0; newnames[n]; n++) + names[n] = newnames[n]; + names[n] = (char *)NULL; + free (newnames); +} + +void +ignore_glob_matches (names) + char **names; +{ + if (globignore.num_ignores == 0) + return; + + ignore_globbed_names (names, glob_name_is_acceptable); +} + +static char * +split_ignorespec (s, ip) + char *s; + int *ip; +{ + char *t; + int n, i; + + i = *ip; + if (s[i] == 0) + return 0; + + n = skip_to_delim (s, i, ":", SD_NOJMP|SD_EXTGLOB); + t = substring (s, i, n); + + if (s[n] == ':') + n++; + *ip = n; + return t; +} + +void +setup_ignore_patterns (ivp) + struct ignorevar *ivp; +{ + int numitems, maxitems, ptr; + char *colon_bit, *this_ignoreval; + struct ign *p; + + this_ignoreval = get_string_value (ivp->varname); + + /* If nothing has changed then just exit now. */ + if ((this_ignoreval && ivp->last_ignoreval && STREQ (this_ignoreval, ivp->last_ignoreval)) || + (!this_ignoreval && !ivp->last_ignoreval)) + return; + + /* Oops. The ignore variable has changed. Re-parse it. */ + ivp->num_ignores = 0; + + if (ivp->ignores) + { + for (p = ivp->ignores; p->val; p++) + free(p->val); + free (ivp->ignores); + ivp->ignores = (struct ign *)NULL; + } + + if (ivp->last_ignoreval) + { + free (ivp->last_ignoreval); + ivp->last_ignoreval = (char *)NULL; + } + + if (this_ignoreval == 0 || *this_ignoreval == '\0') + return; + + ivp->last_ignoreval = savestring (this_ignoreval); + + numitems = maxitems = ptr = 0; + +#if 0 + while (colon_bit = extract_colon_unit (this_ignoreval, &ptr)) +#else + while (colon_bit = split_ignorespec (this_ignoreval, &ptr)) +#endif + { + if (numitems + 1 >= maxitems) + { + maxitems += 10; + ivp->ignores = (struct ign *)xrealloc (ivp->ignores, maxitems * sizeof (struct ign)); + } + ivp->ignores[numitems].val = colon_bit; + ivp->ignores[numitems].len = strlen (colon_bit); + ivp->ignores[numitems].flags = 0; + if (ivp->item_func) + (*ivp->item_func) (&ivp->ignores[numitems]); + numitems++; + } + ivp->ignores[numitems].val = (char *)NULL; + ivp->num_ignores = numitems; +} @@ -1,128 +1,134 @@ -# Hungarian translation of the GNU bash. -# Copyright (C) 2002 Free Software Foundation, Inc. -# Gábor István <stive@mezobereny.hu>, 2002. +# Hungarian translation for bash. +# Copyright (C) 2010 Free Software Foundation, Inc. +# This file is distributed under the same license as the bash package. # +# Mate Ory <orymate@ubuntu.com>, 2010. msgid "" msgstr "" -"Project-Id-Version: bash 2.0\n" +"Project-Id-Version: bash-4.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2009-12-30 08:25-0500\n" -"PO-Revision-Date: 2002-06-14 09:49GMT\n" -"Last-Translator: Gábor István <stive@mezobereny.hu>\n" +"PO-Revision-Date: 2010-08-06 17:44+0200\n" +"Last-Translator: Mate Ory <orymate@ubuntu.com>\n" "Language-Team: Hungarian <translation-team-hu@lists.sourceforge.net>\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: KBabel 0.9.5\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" #: arrayfunc.c:50 msgid "bad array subscript" -msgstr "hibás tömb a tömbindexben" +msgstr "Hibás tömbindex" #: arrayfunc.c:313 builtins/declare.def:481 #, c-format msgid "%s: cannot convert indexed to associative array" -msgstr "" +msgstr "%s: nem lehetséges az indexelt tömb asszociatÃvvá alakÃtása" #: arrayfunc.c:480 -#, fuzzy, c-format +#, c-format msgid "%s: invalid associative array key" -msgstr "%c%c: rossz opció" +msgstr "%s: érvénytelen asszociatÃvtömb-index" #: arrayfunc.c:482 #, c-format msgid "%s: cannot assign to non-numeric index" -msgstr "%s: nem lehet hozzárendelni nem szám indexet" +msgstr "%s: a nem-szám indexnek való értékadás nem lehetséges" #: arrayfunc.c:518 #, c-format msgid "%s: %s: must use subscript when assigning associative array" -msgstr "" +msgstr "%s: %s: asszociatÃv tömbhöz való értékadásnál meg kell adni az indexet" #: bashhist.c:383 #, c-format msgid "%s: cannot create: %s" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: nem hozható létre: %s" #: bashline.c:3457 msgid "bash_execute_unix_command: cannot find keymap for command" -msgstr "" +msgstr "bash_execute_unix_command: nem található billentyűkiosztás a parancshoz" #: bashline.c:3543 #, c-format msgid "%s: first non-whitespace character is not `\"'" -msgstr "" +msgstr "%s: az elsÅ‘ nem szóközkarakter nem „\"â€" #: bashline.c:3572 #, c-format msgid "no closing `%c' in %s" -msgstr "" +msgstr "nincs záró „%c†a következÅ‘ben: %s" #: bashline.c:3606 #, c-format msgid "%s: missing colon separator" -msgstr "" +msgstr "%s: hiányzó kettÅ‘spont-elválasztó" #: builtins/alias.def:132 -#, fuzzy, c-format +#, c-format msgid "`%s': invalid alias name" -msgstr "%c%c: rossz opció" +msgstr "„%sâ€: érvénytelen alias-név" #: builtins/bind.def:120 builtins/bind.def:123 msgid "line editing not enabled" -msgstr "" +msgstr "nincs engedélyezve a sorszerkesztés" #: builtins/bind.def:206 #, c-format msgid "`%s': invalid keymap name" -msgstr "" +msgstr "„%sâ€: érvénytelen billentyűkiosztás-név" #: builtins/bind.def:245 -#, fuzzy, c-format +#, c-format msgid "%s: cannot read: %s" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: nem olvasható a következÅ‘: %s" #: builtins/bind.def:260 -#, fuzzy, c-format +#, c-format msgid "`%s': cannot unbind" -msgstr "%s: parancs nem található" +msgstr "„%sâ€: nem lehetséges a kötés megszüntetése" #: builtins/bind.def:295 builtins/bind.def:325 -#, fuzzy, c-format +#, c-format msgid "`%s': unknown function name" -msgstr "%s Csak olvasható funkció" +msgstr "„%sâ€: ismeretlen függvénynév" #: builtins/bind.def:303 #, c-format msgid "%s is not bound to any keys.\n" -msgstr "" +msgstr "%s nincs kötve egy billentyűhöz sem.\n" #: builtins/bind.def:307 #, c-format msgid "%s can be invoked via " -msgstr "" +msgstr "%s a következÅ‘ módon hajtható végre: " #: builtins/break.def:77 builtins/break.def:117 -#, fuzzy msgid "loop count" -msgstr "logout" +msgstr "ciklusszám" #: builtins/break.def:137 msgid "only meaningful in a `for', `while', or `until' loop" -msgstr "" +msgstr "csak „forâ€, „while†és „until†ciklusokban értelmezhetÅ‘" +# see $ help caller #: builtins/caller.def:133 msgid "" "Returns the context of the current subroutine call.\n" " \n" " Without EXPR, returns " msgstr "" +"Az aktuális szubrutinhÃvás helyével tér vissza.\n" +" \n" +" EXPR nélkül a " #: builtins/caller.def:135 msgid "" ". With EXPR, returns\n" " " msgstr "" +"kifejezéssel, EXPR-rel a\n" +" " #: builtins/caller.def:136 msgid "" @@ -132,345 +138,344 @@ msgid "" " The value of EXPR indicates how many call frames to go back before the\n" " current one; the top frame is frame 0." msgstr "" +" kifejezéssel tér vissza. Ez az adat stack trace kiÃrásához\n" +" lehet hasznos.\n" +" \n" +" Az EXPR értéke azt adja meg, hogy a jelenlegihez képest milyen mélyre\n" +" lépjen vissza; a verem tetején a 0-s keret van." #: builtins/cd.def:215 msgid "HOME not set" -msgstr "" +msgstr "Nincs beállÃtva HOME" #: builtins/cd.def:227 msgid "OLDPWD not set" -msgstr "" +msgstr "Nincs beállÃtva OLDPWD" #: builtins/common.c:101 -#, fuzzy, c-format +#, c-format msgid "line %d: " -msgstr "foglalat %3d: " +msgstr "%d. sor: " #: builtins/common.c:139 error.c:261 -#, fuzzy, c-format +#, c-format msgid "warning: " -msgstr "írás" +msgstr "figyelmeztetés: " #: builtins/common.c:153 #, c-format msgid "%s: usage: " -msgstr "" +msgstr "%s: használat: " #: builtins/common.c:166 test.c:827 msgid "too many arguments" -msgstr "túl sok argumentum" +msgstr "túl sok argumentum" #: builtins/common.c:191 shell.c:499 shell.c:782 -#, fuzzy, c-format +#, c-format msgid "%s: option requires an argument" -msgstr "az opció paramétert igényel:-" +msgstr "%s: a kapcsolónak kötelezÅ‘ argumentuma van" #: builtins/common.c:198 #, c-format msgid "%s: numeric argument required" -msgstr "" +msgstr "%s: a kötelezÅ‘ argumentum egy szám" #: builtins/common.c:205 -#, fuzzy, c-format +#, c-format msgid "%s: not found" -msgstr "%s: parancs nem található" +msgstr "%s: nem található" #: builtins/common.c:214 shell.c:795 -#, fuzzy, c-format +#, c-format msgid "%s: invalid option" -msgstr "%c%c: rossz opció" +msgstr "%s: érvénytelen kapcsoló" #: builtins/common.c:221 -#, fuzzy, c-format +#, c-format msgid "%s: invalid option name" -msgstr "%c%c: rossz opció" +msgstr "%s: érvénytelen kapcsolónév" #: builtins/common.c:228 general.c:231 general.c:236 -#, fuzzy, c-format +#, c-format msgid "`%s': not a valid identifier" -msgstr "`%s' nem érvényes azonosító" +msgstr "„%sâ€: érvénytelen azonosÃtó" #: builtins/common.c:238 -#, fuzzy msgid "invalid octal number" -msgstr "rossz jel(signal) szám" +msgstr "érvénytelen oktális szám" #: builtins/common.c:240 -#, fuzzy msgid "invalid hex number" -msgstr "rossz jel(signal) szám" +msgstr "érvénytelen hexadecimális szám" #: builtins/common.c:242 expr.c:1256 -#, fuzzy msgid "invalid number" -msgstr "rossz jel(signal) szám" +msgstr "érvénytelen szám" #: builtins/common.c:250 #, c-format msgid "%s: invalid signal specification" -msgstr "" +msgstr "%s: érvénytelen szignálmegadás" #: builtins/common.c:257 #, c-format msgid "`%s': not a pid or valid job spec" -msgstr "" +msgstr "„%sâ€: nem pid vagy munkaazonosÃtó" #: builtins/common.c:264 error.c:454 #, c-format msgid "%s: readonly variable" -msgstr "%s Csak olvasható változó" +msgstr "%s: csak olvasható változó" #: builtins/common.c:272 #, c-format msgid "%s: %s out of range" -msgstr "" +msgstr "%s: %s kÃvül esik a tartományon" #: builtins/common.c:272 builtins/common.c:274 -#, fuzzy msgid "argument" -msgstr "paraméter szükséges" +msgstr "argumentum" #: builtins/common.c:274 #, c-format msgid "%s out of range" -msgstr "" +msgstr "%s kÃvül esik a tartományon" #: builtins/common.c:282 #, c-format msgid "%s: no such job" -msgstr "" +msgstr "%s: nincs ilyen munka" #: builtins/common.c:290 -#, fuzzy, c-format +#, c-format msgid "%s: no job control" -msgstr "nincs munkafolyamat ellenõrzés ezen a parancsértelmezõn" +msgstr "%s: nincs munkakezelés" #: builtins/common.c:292 -#, fuzzy msgid "no job control" -msgstr "nincs munkafolyamat ellenõrzés ezen a parancsértelmezõn" +msgstr "nincs munkakezelés" #: builtins/common.c:302 -#, fuzzy, c-format +#, c-format msgid "%s: restricted" -msgstr "%s: munkafolyamat megszakadt" +msgstr "%s: korlátozott" #: builtins/common.c:304 -#, fuzzy msgid "restricted" -msgstr "Félbeszakítva" +msgstr "korlátozott" #: builtins/common.c:312 #, c-format msgid "%s: not a shell builtin" -msgstr "" +msgstr "%s: nem beépÃtett parancs" #: builtins/common.c:321 -#, fuzzy, c-format +#, c-format msgid "write error: %s" -msgstr "Csõ (pipe)hiba %s" +msgstr "Ãrási hiba: %s" #: builtins/common.c:329 #, c-format msgid "error setting terminal attributes: %s" -msgstr "" +msgstr "hiba a terminálattribútum beállÃtásakor: %s" #: builtins/common.c:331 #, c-format msgid "error getting terminal attributes: %s" -msgstr "" +msgstr "hiba a terminálattribútum lekérdezésekor: %s" #: builtins/common.c:563 #, c-format msgid "%s: error retrieving current directory: %s: %s\n" -msgstr "" +msgstr "%s: hiba a munkakönyvtár lekérdezésekor: %s: %s\n" #: builtins/common.c:629 builtins/common.c:631 -#, fuzzy, c-format +#, c-format msgid "%s: ambiguous job spec" -msgstr "%s: Nem egyértelmû átirányítás" +msgstr "%s: kétértelmű munkamegadás" #: builtins/complete.def:276 #, c-format msgid "%s: invalid action name" -msgstr "" +msgstr "%s: érvénytelen műveletnév" #: builtins/complete.def:449 builtins/complete.def:644 #: builtins/complete.def:853 #, c-format msgid "%s: no completion specification" -msgstr "" +msgstr "%s: nincs kiegészÃtés meghatározva" #: builtins/complete.def:696 msgid "warning: -F option may not work as you expect" -msgstr "" +msgstr "figyelmeztetés: a -F kapcsoló nem a várt módon működhet" #: builtins/complete.def:698 msgid "warning: -C option may not work as you expect" -msgstr "" +msgstr "figyelmeztetés: a -C kapcsoló nem a várt módon működhet" #: builtins/complete.def:826 msgid "not currently executing completion function" -msgstr "" +msgstr "jelenleg nincs kiegészÃtési függvény végrehajtás alatt" #: builtins/declare.def:122 -#, fuzzy msgid "can only be used in a function" -msgstr "A local-t csak funkción belül lehet használni, létrehozott változó NÉV" +msgstr "csak függvényben használható" #: builtins/declare.def:360 msgid "cannot use `-f' to make functions" -msgstr "" +msgstr "nem használható a „-f†függvény létrehozására" #: builtins/declare.def:372 execute_cmd.c:4937 #, c-format msgid "%s: readonly function" -msgstr "%s Csak olvasható funkció" +msgstr "%s: csak olvasható függvény" #: builtins/declare.def:468 -#, fuzzy, c-format +#, c-format msgid "%s: cannot destroy array variables in this way" -msgstr "$%s így nem lehet hozzárendelni" +msgstr "%s: ilyen módon nem lehet tömböt megszüntetni" #: builtins/declare.def:475 #, c-format msgid "%s: cannot convert associative to indexed array" -msgstr "" +msgstr "%s: nem lehetséges az asszociatÃv tömb indexeltté alakÃtása" #: builtins/enable.def:137 builtins/enable.def:145 msgid "dynamic loading not available" -msgstr "" +msgstr "a dinamikus betöltés nem érhetÅ‘ el" #: builtins/enable.def:312 -#, fuzzy, c-format +#, c-format msgid "cannot open shared object %s: %s" -msgstr "nem lehet létrehozni a %s \"named pipe\"-ot a %s-nek: %s" +msgstr "megosztott objektumfájl megnyitása sikertelen: %s: %s" #: builtins/enable.def:335 #, c-format msgid "cannot find %s in shared object %s: %s" -msgstr "" +msgstr "%s nem található a(z) %s megosztott objektumfájlban: %s" #: builtins/enable.def:459 #, c-format msgid "%s: not dynamically loaded" -msgstr "" +msgstr "%s: nem dinamikusan van betöltve" #: builtins/enable.def:474 -#, fuzzy, c-format +#, c-format msgid "%s: cannot delete: %s" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: nem törölhetÅ‘: %s" #: builtins/evalfile.c:134 builtins/hash.def:169 execute_cmd.c:4794 #: shell.c:1452 #, c-format msgid "%s: is a directory" -msgstr "%s: egy könyvtár" +msgstr "%s egy könyvtár" #: builtins/evalfile.c:139 -#, fuzzy, c-format +#, c-format msgid "%s: not a regular file" -msgstr "%s: nem futtatható bináris fájl" +msgstr "%s: nem normál fájl" #: builtins/evalfile.c:147 #, c-format msgid "%s: file is too large" -msgstr "" +msgstr "%s: a fájl túl nagy" #: builtins/evalfile.c:185 builtins/evalfile.c:203 execute_cmd.c:4864 #: shell.c:1462 #, c-format msgid "%s: cannot execute binary file" -msgstr "%s: nem futtatható bináris fájl" +msgstr "%s: bináris nem hajtható végre" #: builtins/exec.def:212 -#, fuzzy, c-format +#, c-format msgid "%s: cannot execute: %s" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: nem hajtható végre: %s" #: builtins/exit.def:65 -#, fuzzy, c-format +#, c-format msgid "logout\n" -msgstr "logout" +msgstr "kijelentkezés\n" #: builtins/exit.def:88 msgid "not login shell: use `exit'" -msgstr "" +msgstr "nem bejelentkezÅ‘ parancsértelmezÅ‘: használja az „exitâ€-et" #: builtins/exit.def:120 #, c-format msgid "There are stopped jobs.\n" -msgstr "" +msgstr "Vannak leállÃtott munkák.\n" #: builtins/exit.def:122 #, c-format msgid "There are running jobs.\n" -msgstr "" +msgstr "Vannak futó munkák.\n" #: builtins/fc.def:262 -#, fuzzy msgid "no command found" -msgstr "%s: parancs nem található" +msgstr "nincs ilyen parancs" #: builtins/fc.def:349 msgid "history specification" -msgstr "" +msgstr "elÅ‘zményválasztás" #: builtins/fc.def:370 -#, fuzzy, c-format +#, c-format msgid "%s: cannot open temp file: %s" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: az átmeneti fájl nem nyitható meg: %s" #: builtins/fg_bg.def:149 builtins/jobs.def:282 msgid "current" -msgstr "" +msgstr "aktuális" #: builtins/fg_bg.def:158 #, c-format msgid "job %d started without job control" -msgstr "" +msgstr "a(z) %d. munka munkakezelés nélkül indult" #: builtins/getopt.c:110 -#, fuzzy, c-format +#, c-format msgid "%s: illegal option -- %c\n" -msgstr "érvénytelen opció: -" +msgstr "%s: érvénytelen kapcsoló – %c\n" #: builtins/getopt.c:111 -#, fuzzy, c-format +#, c-format msgid "%s: option requires an argument -- %c\n" -msgstr "az opció paramétert igényel:-" +msgstr "%s: a kapcsolónak kötelezÅ‘ argumentuma van – %c\n" #: builtins/hash.def:92 msgid "hashing disabled" -msgstr "" +msgstr "a hashelés le van tiltva" #: builtins/hash.def:138 #, c-format msgid "%s: hash table empty\n" -msgstr "" +msgstr "%s: a hashtábla üres\n" #: builtins/hash.def:244 -#, fuzzy, c-format +#, c-format msgid "hits\tcommand\n" -msgstr "újra futtathatja az utolsó parancsot." +msgstr "t.szám\tparancs\n" +# fuck. #: builtins/help.def:130 #, c-format msgid "Shell commands matching keyword `" msgid_plural "Shell commands matching keywords `" -msgstr[0] "" -msgstr[1] "" +msgstr[0] "A következÅ‘ kifejezésre illeszkedÅ‘ parancsok: „" +msgstr[1] "A következÅ‘ kifejezésekre illeszkedÅ‘ parancsok: „" #: builtins/help.def:168 #, c-format -msgid "" -"no help topics match `%s'. Try `help help' or `man -k %s' or `info %s'." +msgid "no help topics match `%s'. Try `help help' or `man -k %s' or `info %s'." msgstr "" +"nem illeszkedik egy szócikk sem a következÅ‘re: „%sâ€.\n" +"A „help helpâ€, „man -k '%s'†vagy „info '%s'†parancsok segÃthetnek." #: builtins/help.def:185 -#, fuzzy, c-format +#, c-format msgid "%s: cannot open: %s" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: nem nyitható meg: %s" #: builtins/help.def:337 #, c-format @@ -483,119 +488,121 @@ msgid "" "A star (*) next to a name means that the command is disabled.\n" "\n" msgstr "" +"Ezek a parancsok be vannak épÃtve a parancsértelmezÅ‘be. A „help†parancs\n" +"listázza Å‘ket. A „help név†a „név†parancsról tájékoztat. Az „info bashâ€\n" +"paranccsal általános információt kap a parancsértelmezÅ‘rÅ‘l. A listán nem\n" +"található parancsokról a „man -k†vagy az „info†adhat felvilágosÃtást.\n" +"\n" +"A parancs nevét követÅ‘ csillag (*) azt jelzi, hogy le van tiltva.\n" +"\n" #: builtins/history.def:154 msgid "cannot use more than one of -anrw" -msgstr "" +msgstr "a következÅ‘ kapcsolók kizárják egymást: -anrw" #: builtins/history.def:186 msgid "history position" -msgstr "" +msgstr "elÅ‘zménypozÃció" #: builtins/history.def:365 -#, fuzzy, c-format +#, c-format msgid "%s: history expansion failed" -msgstr "%s egész szám szükséges" +msgstr "%s: sikertelen elÅ‘zménybÅ‘l való kiegészÃtés" #: builtins/inlib.def:71 -#, fuzzy, c-format +#, c-format msgid "%s: inlib failed" -msgstr "%s egész szám szükséges" +msgstr "%s: sikertelen inlib" #: builtins/jobs.def:109 msgid "no other options allowed with `-x'" -msgstr "" +msgstr "a „-x†mellett nem használható más kapcsoló" #: builtins/kill.def:200 #, c-format msgid "%s: arguments must be process or job IDs" -msgstr "" +msgstr "%s: az argumentumok folyamat- vagy munkaazonosÃtók lehetnek" #: builtins/kill.def:263 -#, fuzzy msgid "Unknown error" -msgstr "Ismeretlen hiba %d" +msgstr "Ismeretlen hiba" #: builtins/let.def:95 builtins/let.def:120 expr.c:501 expr.c:516 msgid "expression expected" -msgstr "várható kifejezés" +msgstr "az értelmezÅ‘ kifejezést várt" #: builtins/mapfile.def:165 -#, fuzzy, c-format +#, c-format msgid "%s: not an indexed array" -msgstr "%s felszabadított változó" +msgstr "%s: nem egy indexelt tömb" #: builtins/mapfile.def:249 builtins/read.def:279 #, c-format msgid "%s: invalid file descriptor specification" -msgstr "" +msgstr "%s: érvénytelen fájlleÃró-megadás" #: builtins/mapfile.def:257 builtins/read.def:286 #, c-format msgid "%d: invalid file descriptor: %s" -msgstr "" +msgstr "%d: érvénytelen fájlleÃró: %s" #: builtins/mapfile.def:266 builtins/mapfile.def:304 -#, fuzzy, c-format +#, c-format msgid "%s: invalid line count" -msgstr "%c%c: rossz opció" +msgstr "%s: sorok száma érvénytelen" #: builtins/mapfile.def:277 -#, fuzzy, c-format +#, c-format msgid "%s: invalid array origin" -msgstr "%c%c: rossz opció" +msgstr "%s: érvénytelen tömbkezdet" #: builtins/mapfile.def:294 -#, fuzzy, c-format +#, c-format msgid "%s: invalid callback quantum" -msgstr "rossz jel(signal) szám" +msgstr "%s: érvénytelen parancshÃvási távolság" #: builtins/mapfile.def:326 -#, fuzzy msgid "empty array variable name" -msgstr "%s felszabadított változó" +msgstr "üres tömbváltozó-név" #: builtins/mapfile.def:347 msgid "array variable support required" -msgstr "" +msgstr "a tömbök használata nincs támogatva" #: builtins/printf.def:374 #, c-format msgid "`%s': missing format character" -msgstr "" +msgstr "„%sâ€: hiányzó formátumkarakter" #: builtins/printf.def:551 #, c-format msgid "`%c': invalid format character" -msgstr "" +msgstr "„%câ€: érvénytelen formátumkarakter" #: builtins/printf.def:578 #, c-format msgid "warning: %s: %s" -msgstr "" +msgstr "figyelmeztetés: %s: %s" #: builtins/printf.def:757 msgid "missing hex digit for \\x" -msgstr "" +msgstr "hiányzó hexadecimális számjegy a következÅ‘höz: \\x" #: builtins/pushd.def:195 -#, fuzzy msgid "no other directory" -msgstr "könyvtárba." +msgstr "nincs másik könyvtár" #: builtins/pushd.def:462 -#, fuzzy msgid "<no current directory>" -msgstr "\tlesz az aktuális munkakönyvtár." +msgstr "<nincs munkakönyvtár>" #: builtins/pushd.def:506 msgid "directory stack empty" -msgstr "" +msgstr "a könyvtárverem üres" #: builtins/pushd.def:508 -#, fuzzy msgid "directory stack index" -msgstr "Rekurziós verem túlcsordult" +msgstr "könyvtárveremindex" #: builtins/pushd.def:683 msgid "" @@ -612,14 +619,26 @@ msgid "" " \twith its position in the stack\n" " \n" " Arguments:\n" -" +N\tDisplays the Nth entry counting from the left of the list shown " -"by\n" +" +N\tDisplays the Nth entry counting from the left of the list shown by\n" " \tdirs when invoked without options, starting with zero.\n" " \n" -" -N\tDisplays the Nth entry counting from the right of the list shown " -"by\n" +" -N\tDisplays the Nth entry counting from the right of the list shown by\n" "\tdirs when invoked without options, starting with zero." msgstr "" +"MegjelenÃti a jelenleg megjegyzett könyvtárakat. A könyvtárakat a „pushdâ€\n" +"paranccsal lehet a verembe rakni; és a „popd†paranccsal kivenni.\n" +" \n" +" Kapcsolók:\n" +" -c a könyvtárverem törlése az összes elem eltávolÃtásával\n" +" -l a saját könyvtárat ne rövidÃtse a listázáskor egy tilde (~)\n" +" -p a könyvtárverem kiÃrása soronként egy elemmel\n" +" -v a könyvtárverem kiÃrása soronként egy elemmel, a vermen\n" +" belüli pozÃció jelölésével\n" +" \n" +" Argumentumok:\n" +" +N N darab bejegyzést jelenÃt meg az argumentum nélkül a dirs\n" +" által megjelenÃtett listán balról számolva, nullától kezdve\n" +" -N N darab bejegyzést jelenÃt meg a listából jobbról számolva" #: builtins/pushd.def:705 msgid "" @@ -645,6 +664,27 @@ msgid "" " \n" " The `dirs' builtin displays the directory stack." msgstr "" +"Egy könyvtárat tesz a könyvtárverem tetejére, vagy forgatja a vermet, az új\n" +"felsÅ‘ elemmé a jelenlegi munkakönyvtárat téve. Argumentumok nélkül hÃvva a\n" +"két felsÅ‘ könyvtárat cseréli meg.\n" +" \n" +" Kapcsolók:\n" +" -n Ne váltson könyvtárat hozzáadáskor, vagyis csak a\n" +" vermet változtassa.\n" +" \n" +" Argumentumok:\n" +" +N Úgy forgatja a vermet, hogy az N-edik könyvtár (0-tól\n" +" kezdve, a „dirs†által kiÃrt listán balról számolva)\n" +" kerüljön a verem tetejére.\n" +" \n" +" -N Úgy forgatja a vermet, hogy az N-edik könyvtár (0-tól\n" +" kezdve, a „dirs†által kiÃrt listán jobbról számolva)\n" +" kerüljön a verem tetejére.\n" +" \n" +" dir A verem tetejére helyezi KTÃR könyvtárat, és ugyanezt\n" +" állÃtja be új munkakönyvtárnak.\n" +" \n" +" A „dirs†beépÃtett parancs listázza a könyvtárvermet." #: builtins/pushd.def:730 msgid "" @@ -666,469 +706,467 @@ msgid "" " \n" " The `dirs' builtin displays the directory stack." msgstr "" +"Elemeket vesz ki a könyvtárverembÅ‘l. Argumentumok nélkül kiveszi a legfel-\n" +"sÅ‘ elemet, és a kivett elemre állÃtja az új munkakönyvtárat.\n" +" \n" +" Kapcsolók:\n" +" -n Ne váltson könyvtárat eltávolÃtáskor, vagyis csak a vermet\n" +" változtassa.\n" +" \n" +" Argumentumok:\n" +" +N EltávolÃtja az N-edik elemet a „dirs†által kiÃrt listán, nullá-\n" +" tól, balról számolva. Pl. a „popd +0†az elsÅ‘, mÃg a „popd +1†a\n" +" könyvtárat távolÃtja el.\n" +" -N EltávolÃtja az N-edik elemet a „dirs†által kiÃrt listán, nullá-\n" +" tól, jobbról számolva. Pl. a „popd -0†az utolsó, a „popd -1†az\n" +" utolsó elÅ‘tti könyvtárat távolÃtja el.\n" +" \n" +" A „dirs†beépÃtett parancs listázza a könyvtárvermet." #: builtins/read.def:252 #, c-format msgid "%s: invalid timeout specification" -msgstr "" +msgstr "%s: érvénytelen idÅ‘korlát-megadás" #: builtins/read.def:588 -#, fuzzy, c-format +#, c-format msgid "read error: %d: %s" -msgstr "Csõ (pipe)hiba %s" +msgstr "olvasási hiba: %d: %s" #: builtins/return.def:73 msgid "can only `return' from a function or sourced script" -msgstr "" +msgstr "csak függvénybÅ‘l vagy source-olt parancsfájlból lehet „returnâ€-nel visszatérni" #: builtins/set.def:768 -#, fuzzy msgid "cannot simultaneously unset a function and a variable" -msgstr "A local-t csak funkción belül lehet használni, létrehozott változó NÉV" +msgstr "nem lehet egyszerre függvényt és változót megszüntetni" #: builtins/set.def:805 -#, fuzzy, c-format +#, c-format msgid "%s: cannot unset" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: nem szüntethetÅ‘ meg" #: builtins/set.def:812 -#, fuzzy, c-format +#, c-format msgid "%s: cannot unset: readonly %s" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: nem szüntethetÅ‘ meg: csak olvasható %s" #: builtins/set.def:823 -#, fuzzy, c-format +#, c-format msgid "%s: not an array variable" -msgstr "%s felszabadított változó" +msgstr "%s: nem egy tömbváltozó" #: builtins/setattr.def:186 -#, fuzzy, c-format +#, c-format msgid "%s: not a function" -msgstr "%s Csak olvasható funkció" +msgstr "%s: nem függvény" #: builtins/shift.def:71 builtins/shift.def:77 -#, fuzzy msgid "shift count" -msgstr "shift [n]" +msgstr "shift-szám" #: builtins/shopt.def:260 msgid "cannot set and unset shell options simultaneously" -msgstr "" +msgstr "nem lehet egyszerre beállÃtani és törölni parancsértelmezÅ‘-beállÃtásokat" #: builtins/shopt.def:325 #, c-format msgid "%s: invalid shell option name" -msgstr "" +msgstr "%s: érvénytelen parancsértelmezÅ‘kapcsoló-név" #: builtins/source.def:128 msgid "filename argument required" -msgstr "" +msgstr "fájlnévargumentum szükséges" #: builtins/source.def:153 -#, fuzzy, c-format +#, c-format msgid "%s: file not found" -msgstr "%s: parancs nem található" +msgstr "%s: a fájl nem található" #: builtins/suspend.def:101 msgid "cannot suspend" -msgstr "" +msgstr "nem lehet szüneteltetni" #: builtins/suspend.def:111 -#, fuzzy msgid "cannot suspend a login shell" -msgstr "Kilépés a parancsértelmezõbõl." +msgstr "nem lehet bejelentkezÅ‘ parancsértelmezÅ‘t szüneteltetni" #: builtins/type.def:234 #, c-format msgid "%s is aliased to `%s'\n" -msgstr "" +msgstr "%s egy alias a következÅ‘re: „%sâ€\n" #: builtins/type.def:255 #, c-format msgid "%s is a shell keyword\n" -msgstr "" +msgstr "%s nem parancsértelmezÅ‘-kulcsszó\n" #: builtins/type.def:274 -#, fuzzy, c-format +#, c-format msgid "%s is a function\n" -msgstr "%s Csak olvasható funkció" +msgstr "%s egy függvény\n" #: builtins/type.def:296 #, c-format msgid "%s is a shell builtin\n" -msgstr "" +msgstr "%s egy beépÃtett parancs\n" #: builtins/type.def:317 builtins/type.def:391 #, c-format msgid "%s is %s\n" -msgstr "" +msgstr "%s egy %s\n" #: builtins/type.def:337 #, c-format msgid "%s is hashed (%s)\n" -msgstr "" +msgstr "%s hashelve van (%s)\n" #: builtins/ulimit.def:372 #, c-format msgid "%s: invalid limit argument" -msgstr "" +msgstr "%s: érvénytelen korlátérték" #: builtins/ulimit.def:398 -#, fuzzy, c-format +#, c-format msgid "`%c': bad command" -msgstr "%c%c: rossz opció" +msgstr "„%câ€: érvénytelen parancs" #: builtins/ulimit.def:427 -#, fuzzy, c-format +#, c-format msgid "%s: cannot get limit: %s" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: nem kérdezhetÅ‘ le a korlát: %s" #: builtins/ulimit.def:453 -#, fuzzy msgid "limit" -msgstr "CPU határ" +msgstr "korlát" #: builtins/ulimit.def:465 builtins/ulimit.def:765 -#, fuzzy, c-format +#, c-format msgid "%s: cannot modify limit: %s" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: nem módosÃtható a korlát: %s" #: builtins/umask.def:118 -#, fuzzy msgid "octal number" -msgstr "rossz jel(signal) szám" +msgstr "oktális szám" #: builtins/umask.def:231 #, c-format msgid "`%c': invalid symbolic mode operator" -msgstr "" +msgstr "„%câ€: érvénytelen szimbolikus módoperátor" #: builtins/umask.def:286 #, c-format msgid "`%c': invalid symbolic mode character" -msgstr "" +msgstr "„%câ€: érvénytelen szimbolikus módkarakter" #: error.c:90 error.c:321 error.c:323 error.c:325 msgid " line " -msgstr "" +msgstr " sor: " #: error.c:165 -#, fuzzy, c-format +#, c-format msgid "last command: %s\n" -msgstr "újra futtathatja az utolsó parancsot." +msgstr "utolsó parancs: %s\n" #: error.c:173 #, c-format msgid "Aborting..." -msgstr "" +msgstr "MegszakÃtás..." #: error.c:406 -#, fuzzy msgid "unknown command error" -msgstr "Ismeretlen hiba %d" +msgstr "hiba: érvénytelen parancs" #: error.c:407 -#, fuzzy msgid "bad command type" -msgstr "parancsként használja." +msgstr "hibás parancstÃpus" #: error.c:408 -#, fuzzy msgid "bad connector" -msgstr "rossz csatlakozás `%d'" +msgstr "hibás csatlakozó" #: error.c:409 -#, fuzzy msgid "bad jump" -msgstr "Rossz ugrás %d" +msgstr "hibás ugrás" #: error.c:447 #, c-format msgid "%s: unbound variable" -msgstr "%s felszabadított változó" +msgstr "%s: kötetlen változó" #: eval.c:181 -#, fuzzy, c-format -msgid "timed out waiting for input: auto-logout\n" -msgstr "%c túl sokáig nem csinált semmit:automatikus kilépés\n" +#, c-format +msgid "\atimed out waiting for input: auto-logout\n" +msgstr "\aidÅ‘túllépés bemenetre várva: automatikus kijelentkezés\n" #: execute_cmd.c:497 #, c-format msgid "cannot redirect standard input from /dev/null: %s" -msgstr "" +msgstr "szabványos bemenet /dev/null-ra állÃtása sikertelen: %s" #: execute_cmd.c:1162 #, c-format msgid "TIMEFORMAT: `%c': invalid format character" -msgstr "" +msgstr "IDÅFORMÃTUM: „%câ€: érvénytelen formátumkarakter" #: execute_cmd.c:2075 -#, fuzzy msgid "pipe error" -msgstr "Csõ (pipe)hiba %s" +msgstr "hibás csÅ‘vezeték" #: execute_cmd.c:4481 #, c-format msgid "%s: restricted: cannot specify `/' in command names" -msgstr "%s: fenntartva: parancs nem tartalmazhat '/' karaktert" +msgstr "%s: korlátozott: nem adható meg „/†a parancsok nevében" #: execute_cmd.c:4572 #, c-format msgid "%s: command not found" -msgstr "%s: parancs nem található" +msgstr "%s: parancs nem található" #: execute_cmd.c:4827 -#, fuzzy, c-format +#, c-format msgid "%s: %s: bad interpreter" -msgstr "%s: egy könyvtár" +msgstr "%s: %s: rossz parancsértelmezÅ‘" #: execute_cmd.c:4976 -#, fuzzy, c-format +#, c-format msgid "cannot duplicate fd %d to fd %d" -msgstr "nem másolható a fd %d fd 0: %s-re" +msgstr "nem lehet duplikálni a(z) %d. fájlleÃrót a(z) %d. helyre" #: expr.c:241 msgid "expression recursion level exceeded" -msgstr "kifejezés túllépte a megengedett rekurzív szintet" +msgstr "a kifejezés rekurziókorlátot" #: expr.c:265 -#, fuzzy msgid "recursion stack underflow" -msgstr "Rekurziós verem túlcsordult" +msgstr "alulcsordult a rekurziós verem" #: expr.c:379 msgid "syntax error in expression" -msgstr "szintaktikai hiba a kifelyezésben" +msgstr "szintaktikai hiba a kifejezésben" #: expr.c:419 msgid "attempted assignment to non-variable" -msgstr "nem változóhoz próbált hozzárendelni" +msgstr "nem változóhoz próbált értéket rendelni" #: expr.c:440 expr.c:445 expr.c:756 msgid "division by 0" -msgstr "nullával való osztás" +msgstr "0-val osztás" #: expr.c:471 -#, fuzzy msgid "bug: bad expassign token" -msgstr "Hiba:rossz vezérjel %d" +msgstr "bug: rossz expassign token" #: expr.c:513 msgid "`:' expected for conditional expression" -msgstr "`:' túllépte a kifelyezés feltételeit" +msgstr "„:†egy feltételkifejezés szükséges" #: expr.c:781 msgid "exponent less than 0" -msgstr "" +msgstr "0-nál kisebb kitevÅ‘" #: expr.c:826 msgid "identifier expected after pre-increment or pre-decrement" -msgstr "" +msgstr "prefix növelés vagy csökkentés után azonosÃtó kell következzen" #: expr.c:854 msgid "missing `)'" -msgstr "rossz ')'" +msgstr "hiányzó „)â€" #: expr.c:897 expr.c:1176 -#, fuzzy msgid "syntax error: operand expected" -msgstr "szintaktikai hiba: váratlan fájl vég" +msgstr "szintaktikai hiba: operandus kell következzen" #: expr.c:1178 msgid "syntax error: invalid arithmetic operator" -msgstr "" +msgstr "szintaktikai hiba: érvénytelen aritmetikai operátor" #: expr.c:1202 -#, fuzzy, c-format +#, c-format msgid "%s%s%s: %s (error token is \"%s\")" -msgstr "%s: %s: %s (a hiba jele \"%s\")\n" +msgstr "%s%s%s: %s (hibás token: „%sâ€)" #: expr.c:1260 msgid "invalid arithmetic base" -msgstr "" +msgstr "érvénytelen számrendszer" #: expr.c:1280 msgid "value too great for base" -msgstr "az érték túl nagy" +msgstr "túl nagy érték a számrendszerhez" #: expr.c:1329 -#, fuzzy, c-format +#, c-format msgid "%s: expression error\n" -msgstr "%s egész szám szükséges" +msgstr "%s: hibás kifejezés\n" #: general.c:61 -#, fuzzy msgid "getcwd: cannot access parent directories" -msgstr "getwd: nem elérhetõ a szülõ könyvtár" +msgstr "getcwd: nem érhetÅ‘ek el a szülÅ‘könyvtárak" #: input.c:94 subst.c:4857 -#, fuzzy, c-format +#, c-format msgid "cannot reset nodelay mode for fd %d" -msgstr "nem másolható a fd %d fd 0: %s-re" +msgstr "nem lehet újraindÃtani a nodelay módot a(z) %d. fájlleÃróhoz" #: input.c:258 -#, fuzzy, c-format +#, c-format msgid "cannot allocate new file descriptor for bash input from fd %d" -msgstr "" -"nem lehet lefoglalni az új fájlleírót a bash bemenetére a fd %d: %s-r?l" +msgstr "nem lehet új fájlleÃrót foglalni a bash bemenetéhez a(z) %d. fájlleÃróból" #: input.c:266 -#, fuzzy, c-format +#, c-format msgid "save_bash_input: buffer already exists for new fd %d" -msgstr "check_bash_input: puffer már létezik az új fd %d" +msgstr "save_bash_input: már van puffer a(z) %d. fájlleÃróhoz" #: jobs.c:466 msgid "start_pipeline: pgrp pipe" -msgstr "" +msgstr "start_pipeline: pgrp csÅ‘vezeték" #: jobs.c:887 #, c-format msgid "forked pid %d appears in running job %d" -msgstr "" +msgstr "a(z) %d számú forkolt pid a(z) %d számú munkában jelent meg" #: jobs.c:1005 #, c-format msgid "deleting stopped job %d with process group %ld" -msgstr "" +msgstr "%d. számú megállÃtott munka törlése a %ld számú folyamatcsoporttal" #: jobs.c:1110 #, c-format msgid "add_process: process %5ld (%s) in the_pipeline" -msgstr "" +msgstr "add_process: %5ld. folyamat (%s) a the_pipeline-ban" #: jobs.c:1113 #, c-format msgid "add_process: pid %5ld (%s) marked as still alive" -msgstr "" +msgstr "add_process: %5ld. folyamat (%s) még élÅ‘ként van jelölve" #: jobs.c:1401 -#, fuzzy, c-format +#, c-format msgid "describe_pid: %ld: no such pid" -msgstr "describe_pid: Nem létezõ pid (%d)!\n" +msgstr "describe_pid: %ld: nincs ilyen pid" #: jobs.c:1416 -#, fuzzy, c-format +#, c-format msgid "Signal %d" -msgstr "Ismeretlen #%d Szignál" +msgstr "%d. szignál" #: jobs.c:1430 jobs.c:1455 msgid "Done" -msgstr "Kész" +msgstr "Kész" #: jobs.c:1435 siglist.c:123 msgid "Stopped" -msgstr "Megállítva" +msgstr "MegállÃtva" #: jobs.c:1439 -#, fuzzy, c-format +#, c-format msgid "Stopped(%s)" -msgstr "Megállítva" +msgstr "MegállÃtva(%s)" #: jobs.c:1443 msgid "Running" -msgstr "Futó" +msgstr "Fut" #: jobs.c:1457 #, c-format msgid "Done(%d)" -msgstr "Kész (%d)" +msgstr "Kész(%d)" #: jobs.c:1459 #, c-format msgid "Exit %d" -msgstr "Kilépés %d" +msgstr "Kilépett(%d)" #: jobs.c:1462 msgid "Unknown status" -msgstr "Ismeretlen állapot" +msgstr "Ismeretlen állapot" #: jobs.c:1549 #, c-format msgid "(core dumped) " -msgstr "(memória kiírás)" +msgstr "(core készült) " #: jobs.c:1568 -#, fuzzy, c-format +#, c-format msgid " (wd: %s)" -msgstr "(wd most: %s)\n" +msgstr " (mk: %s)" #: jobs.c:1776 -#, fuzzy, c-format +#, c-format msgid "child setpgid (%ld to %ld)" -msgstr "gyermek-folyamat setpgid (%d -ról %d-ra) hiba %d: %s\n" +msgstr "gyermek setpgid (innen: %ld ide: %ld)" #: jobs.c:2104 nojobs.c:585 -#, fuzzy, c-format +#, c-format msgid "wait: pid %ld is not a child of this shell" -msgstr "várjon:a %d nem utóda ennek a parancsértelmezõnek" +msgstr "wait: %ld. számú folyamat nem gyermeke ennek a parancsértelmezÅ‘nek" #: jobs.c:2331 #, c-format msgid "wait_for: No record of process %ld" -msgstr "" +msgstr "wait_for: Nincs bejegyzés %ld. számú folyamatról" #: jobs.c:2607 #, c-format msgid "wait_for_job: job %d is stopped" -msgstr "" +msgstr "wait_for_job: %d. számú munka le lett állÃtva" #: jobs.c:2829 #, c-format msgid "%s: job has terminated" -msgstr "%s: munkafolyamat megszakadt" +msgstr "%s: a munka be lett fejezve" #: jobs.c:2838 #, c-format msgid "%s: job %d already in background" -msgstr "" +msgstr "%s: %d. számú munka már a háttérben van" #: jobs.c:3059 msgid "waitchld: turning on WNOHANG to avoid indefinite block" -msgstr "" +msgstr "waitchld: WNOHANG bekapcsolása a korlátlan blokk elkerülésére" #: jobs.c:3508 -#, fuzzy, c-format +#, c-format msgid "%s: line %d: " -msgstr "foglalat %3d: " +msgstr "%s: %d. sor: " #: jobs.c:3522 nojobs.c:814 #, c-format msgid " (core dumped)" -msgstr "(memória kiírás)" +msgstr " (core készült)" #: jobs.c:3534 jobs.c:3547 #, c-format msgid "(wd now: %s)\n" -msgstr "(wd most: %s)\n" +msgstr "(mk most: %s)\n" #: jobs.c:3579 -#, fuzzy msgid "initialize_job_control: getpgrp failed" -msgstr "initialize_jobs: getpgrp sikertelen: %s" +msgstr "initialize_job_control: getpgrp sikertelen" #: jobs.c:3639 -#, fuzzy msgid "initialize_job_control: line discipline" -msgstr "initialize_jobs: sor fegyelem %s" +msgstr "initialize_job_control: line discipline" #: jobs.c:3649 -#, fuzzy msgid "initialize_job_control: setpgid" -msgstr "initialize_jobs: getpgrp sikertelen: %s" +msgstr "initialize_job_control: setpgid" #: jobs.c:3677 #, c-format msgid "cannot set terminal process group (%d)" -msgstr "" +msgstr "nem állÃtható be a terminál folyamatcsoportja (%d)" #: jobs.c:3682 msgid "no job control in this shell" -msgstr "nincs munkafolyamat ellenõrzés ezen a parancsértelmezõn" +msgstr "nincsen munkakezelés ebben a parancsértelmezÅ‘ben" #: lib/malloc/malloc.c:296 #, c-format msgid "malloc: failed assertion: %s\n" -msgstr "" +msgstr "malloc: nem teljesülÅ‘ feltételezés: %s\n" #: lib/malloc/malloc.c:312 #, c-format @@ -1136,231 +1174,226 @@ msgid "" "\r\n" "malloc: %s:%d: assertion botched\r\n" msgstr "" +"\r\n" +"malloc: %s:%d: téves feltételezés\r\n" #: lib/malloc/malloc.c:313 -#, fuzzy msgid "unknown" -msgstr "<ismeretlen>" +msgstr "ismeretlen" #: lib/malloc/malloc.c:797 msgid "malloc: block on free list clobbered" -msgstr "" +msgstr "malloc: a szabadlistán lévÅ‘ blokk felülÃrva" #: lib/malloc/malloc.c:874 msgid "free: called with already freed block argument" -msgstr "" +msgstr "free: már felszabadÃtott blokkal lett hÃvva" #: lib/malloc/malloc.c:877 msgid "free: called with unallocated block argument" -msgstr "" +msgstr "free: nem lefoglalt blokkal lett hÃvva" #: lib/malloc/malloc.c:896 msgid "free: underflow detected; mh_nbytes out of range" -msgstr "" +msgstr "free: alulcsordulást érzékelt; mh_nbytes kÃvül esik a tartományon" #: lib/malloc/malloc.c:902 msgid "free: start and end chunk sizes differ" -msgstr "" +msgstr "free: kezdÅ‘- és záródarab mérete eltér" #: lib/malloc/malloc.c:1001 msgid "realloc: called with unallocated block argument" -msgstr "" +msgstr "realloc: nem lefoglalt blokkal lett hÃvva" #: lib/malloc/malloc.c:1016 msgid "realloc: underflow detected; mh_nbytes out of range" -msgstr "" +msgstr "realloc: alulcsordulást érzékelt; mh_nbytes kÃvül esik a tartományon" #: lib/malloc/malloc.c:1022 msgid "realloc: start and end chunk sizes differ" -msgstr "" +msgstr "realloc: kezdÅ‘- és záródarab mérete eltér" #: lib/malloc/table.c:177 #, c-format msgid "register_alloc: alloc table is full with FIND_ALLOC?\n" -msgstr "" +msgstr "register_alloc: foglalótábla tele van FIND_ALLOC-kal?\n" #: lib/malloc/table.c:184 #, c-format msgid "register_alloc: %p already in table as allocated?\n" -msgstr "" +msgstr "register_alloc: %p már a táblában lefoglaltként?\n" #: lib/malloc/table.c:220 #, c-format msgid "register_free: %p already in table as free?\n" -msgstr "" +msgstr "register_free: %p már a táblában szabadként?\n" #: lib/sh/fmtulong.c:101 msgid "invalid base" -msgstr "" +msgstr "érvénytelen számrendszer" #: lib/sh/netopen.c:168 -#, fuzzy, c-format +#, c-format msgid "%s: host unknown" -msgstr "ismeretlen" +msgstr "%s: ismeretlen gépnév" #: lib/sh/netopen.c:175 #, c-format msgid "%s: invalid service" -msgstr "" +msgstr "%s: érvénytelen szolgáltatás" #: lib/sh/netopen.c:306 #, c-format msgid "%s: bad network path specification" -msgstr "" +msgstr "%s: hibás hálózatiútvonal-megadás" #: lib/sh/netopen.c:346 msgid "network operations not supported" -msgstr "" +msgstr "a hálózati műveletek nincsenek támogatva" #: locale.c:192 #, c-format msgid "setlocale: LC_ALL: cannot change locale (%s)" -msgstr "" +msgstr "setlocale: LC_ALL: nem lehet területi beállÃtásokat váltani (%s)" #: locale.c:194 #, c-format msgid "setlocale: LC_ALL: cannot change locale (%s): %s" -msgstr "" +msgstr "setlocale: LC_ALL: nem lehet területi beállÃtásokat váltani (%s): %s" #: locale.c:247 -#, fuzzy, c-format +#, c-format msgid "setlocale: %s: cannot change locale (%s)" -msgstr "xmalloc: nem lehet lefoglalni %lu bájtot (%lu bájt lefoglalva)" +msgstr "setlocale: %s: nem lehet területi beállÃtásokat váltani (%s)" #: locale.c:249 -#, fuzzy, c-format +#, c-format msgid "setlocale: %s: cannot change locale (%s): %s" -msgstr "xmalloc: nem lehet lefoglalni %lu bájtot (%lu bájt lefoglalva)" +msgstr "setlocale: %s: nem lehet területi beállÃtásokat váltani (%s): %s" #: mailcheck.c:433 msgid "You have mail in $_" -msgstr "Önnek levele van $_ -ben" +msgstr "Levél a következÅ‘ben: $_" #: mailcheck.c:458 msgid "You have new mail in $_" -msgstr "Önnek új levele érkezett a $_ -ra" +msgstr "Új levél a következÅ‘ben: $_" #: mailcheck.c:474 #, c-format msgid "The mail in %s has been read\n" -msgstr "A %s levelet már elolvasta\n" +msgstr "A(z) „%s†helyen lévÅ‘ levél el van olvasva\n" #: make_cmd.c:323 -#, fuzzy msgid "syntax error: arithmetic expression required" -msgstr "szintaktikai hiba a kifelyezésben" +msgstr "szintaktikai hiba: aritmetikai kifejezés szükséges" #: make_cmd.c:325 -#, fuzzy msgid "syntax error: `;' unexpected" -msgstr "szintaktikai hiba: váratlan fájl vég" +msgstr "szintaktikai hiba: váratlan „;â€" #: make_cmd.c:326 -#, fuzzy, c-format +#, c-format msgid "syntax error: `((%s))'" -msgstr "szintaktikai hiba" +msgstr "szintaktikai hiba: „((%s))â€" #: make_cmd.c:575 #, c-format msgid "make_here_document: bad instruction type %d" -msgstr "make_here_document: rossz utasítás típus %d" +msgstr "make_here_document: hibás utasÃtástÃpus: %d" #: make_cmd.c:659 #, c-format msgid "here-document at line %d delimited by end-of-file (wanted `%s')" -msgstr "" +msgstr "a(z) %d. sorban kezdett heredocot EOF zárja („%s†helyett)" #: make_cmd.c:756 #, c-format msgid "make_redirection: redirection instruction `%d' out of range" -msgstr "" +msgstr "make_redirection: %d. átirányÃtó utasÃtás kÃvül esik a tartományon" #: parse.y:3133 parse.y:3369 -#, fuzzy, c-format +#, c-format msgid "unexpected EOF while looking for matching `%c'" -msgstr "váratlan EOF amíg vizsgáltam a `%c'-t" +msgstr "váratlan EOF „%c†helyett" #: parse.y:3951 -#, fuzzy msgid "unexpected EOF while looking for `]]'" -msgstr "váratlan EOF amíg vizsgáltam a `%c'-t" +msgstr "váratlan EOF „]]†helyett" #: parse.y:3956 -#, fuzzy, c-format +#, c-format msgid "syntax error in conditional expression: unexpected token `%s'" -msgstr "szintaktikai hiba a váratlan %s vezérjel körül" +msgstr "szintaktikai hiba a feltételben: váratlan token: „%sâ€" #: parse.y:3960 -#, fuzzy msgid "syntax error in conditional expression" -msgstr "szintaktikai hiba a kifelyezésben" +msgstr "szintaktikai hiba a feltételben" #: parse.y:4038 #, c-format msgid "unexpected token `%s', expected `)'" -msgstr "" +msgstr "váratlan token (%s) „)†helyett" #: parse.y:4042 -#, fuzzy msgid "expected `)'" -msgstr "')' szükséges" +msgstr "„)†szükséges" #: parse.y:4070 #, c-format msgid "unexpected argument `%s' to conditional unary operator" -msgstr "" +msgstr "váratlan argumentum (%s) feltételes egyoperandusú operátorhoz" #: parse.y:4074 msgid "unexpected argument to conditional unary operator" -msgstr "" +msgstr "váratlan argumentum feltételes egyoperandusú operátorhoz" #: parse.y:4120 -#, fuzzy, c-format +#, c-format msgid "unexpected token `%s', conditional binary operator expected" -msgstr "%s:bináris mûvelet szükséges" +msgstr "váratlan token (%s), feltételes kétoperandusú operátor szükséges" #: parse.y:4124 -#, fuzzy msgid "conditional binary operator expected" -msgstr "%s:bináris mûvelet szükséges" +msgstr "feltételes kétoperandusú operátor szükséges" #: parse.y:4146 #, c-format msgid "unexpected argument `%s' to conditional binary operator" -msgstr "" +msgstr "váratlan argumentum (%s) feltételes kétoperandusú operátorhoz" #: parse.y:4150 msgid "unexpected argument to conditional binary operator" -msgstr "" +msgstr "váratlan argumentum feltételes kétoperandusú operátorhoz" #: parse.y:4161 -#, fuzzy, c-format +#, c-format msgid "unexpected token `%c' in conditional command" -msgstr "`:' túllépte a kifelyezés feltételeit" +msgstr "váratlan token (%c) feltételes parancsban" #: parse.y:4164 -#, fuzzy, c-format +#, c-format msgid "unexpected token `%s' in conditional command" -msgstr "`:' túllépte a kifelyezés feltételeit" +msgstr "váratlan token (%s) feltételes parancsban" #: parse.y:4168 -#, fuzzy, c-format +#, c-format msgid "unexpected token %d in conditional command" -msgstr "`:' túllépte a kifelyezés feltételeit" +msgstr "váratlan token (%d) feltételes parancsban" #: parse.y:5459 #, c-format msgid "syntax error near unexpected token `%s'" -msgstr "szintaktikai hiba a váratlan %s vezérjel körül" +msgstr "szintaktikai hiba „%s†váratlan token közelében" #: parse.y:5477 -#, fuzzy, c-format +#, c-format msgid "syntax error near `%s'" -msgstr "szintaktikai hiba a váratlan %s vezérjel körül" +msgstr "szintaktikai hiba „%s†közelében" #: parse.y:5487 msgid "syntax error: unexpected end of file" -msgstr "szintaktikai hiba: váratlan fájl vég" +msgstr "szintaktikai hiba: váratlan fájlvége" #: parse.y:5487 msgid "syntax error" @@ -1369,106 +1402,104 @@ msgstr "szintaktikai hiba" #: parse.y:5549 #, c-format msgid "Use \"%s\" to leave the shell.\n" -msgstr "Használja \"%s\" a parancsértelmezõ elhagyásához.\n" +msgstr "„%s†használatával lehet elhagyni a parancsértelmezÅ‘t.\n" #: parse.y:5711 -#, fuzzy msgid "unexpected EOF while looking for matching `)'" -msgstr "váratlan EOF amíg vizsgáltam a `%c'-t" +msgstr "váratlan EOF „)†helyett" #: pcomplete.c:1030 #, c-format msgid "completion: function `%s' not found" -msgstr "" +msgstr "kiegészÃtés: nem található „%s†függvény" #: pcomplib.c:179 #, c-format msgid "progcomp_insert: %s: NULL COMPSPEC" -msgstr "" +msgstr "progcomp_insert: %s: NULL COMPSPEC" #: print_cmd.c:290 #, c-format msgid "print_command: bad connector `%d'" -msgstr "print_command: rossz csatlakozás `%d'" +msgstr "print_command: hibás csatlakozó (%d)" #: print_cmd.c:363 #, c-format msgid "xtrace_set: %d: invalid file descriptor" -msgstr "" +msgstr "xtrace_set: %d: érvénytelen fájlleÃró" #: print_cmd.c:368 msgid "xtrace_set: NULL file pointer" -msgstr "" +msgstr "xtrace_set: NULL fájlmutató" #: print_cmd.c:372 #, c-format msgid "xtrace fd (%d) != fileno xtrace fp (%d)" -msgstr "" +msgstr "xtrace fd (%d) != fileno xtrace fp (%d)" #: print_cmd.c:1461 #, c-format msgid "cprintf: `%c': invalid format character" -msgstr "" +msgstr "cprintf: „%câ€: érvénytelen formátumkarakter" #: redir.c:110 msgid "file descriptor out of range" -msgstr "" +msgstr "fájlleÃró kÃvül esik a tartományon" #: redir.c:166 -#, fuzzy, c-format +#, c-format msgid "%s: ambiguous redirect" -msgstr "%s: Nem egyértelmû átirányítás" +msgstr "%s: kétértelmű átirányÃtás" #: redir.c:170 -#, fuzzy, c-format +#, c-format msgid "%s: cannot overwrite existing file" -msgstr "%s: Nem lehet megsemmisíteni létezõ fájlt‘" +msgstr "%s: nem lehet felülÃrni létezÅ‘ fájlt" #: redir.c:175 -#, fuzzy, c-format +#, c-format msgid "%s: restricted: cannot redirect output" -msgstr "%s: fenntartva: parancs nem tartalmazhat '/' karaktert" +msgstr "%s: korlátozott: nem lehet átirányÃtani a kimenetet" #: redir.c:180 -#, fuzzy, c-format +#, c-format msgid "cannot create temp file for here-document: %s" -msgstr "nem lehet létrehozni a pipe-ot feladat behelyettesítéshez: %s" +msgstr "nem lehet a heredocnak átmeneti fájlt létrehozni: %s" #: redir.c:184 -#, fuzzy, c-format +#, c-format msgid "%s: cannot assign fd to variable" -msgstr "%s: nem lehet a listához rendelni az elemet" +msgstr "%s: nem lehet változóhoz fájlleÃrót rendelni" #: redir.c:544 msgid "/dev/(tcp|udp)/host/port not supported without networking" -msgstr "" +msgstr "/dev/(tcp|udp)/host/port nincs támogatva hálózat nélkül" #: redir.c:1101 -#, fuzzy msgid "redirection error: cannot duplicate fd" -msgstr "átirányítási hiba" +msgstr "átirányÃtási hiba: nem lehet duplikálni a fájlleÃrót" #: shell.c:332 msgid "could not find /tmp, please create!" -msgstr "" +msgstr "nem található /tmp, hozza létre!" #: shell.c:336 msgid "/tmp must be a valid directory name" -msgstr "" +msgstr "/tmp érvényes könyvtárnév kell legyen" #: shell.c:884 -#, fuzzy, c-format +#, c-format msgid "%c%c: invalid option" -msgstr "%c%c: rossz opció" +msgstr "%c%c: érvénytelen kapcsoló" #: shell.c:1651 msgid "I have no name!" msgstr "Nincs nevem!" #: shell.c:1793 -#, fuzzy, c-format +#, c-format msgid "GNU bash, version %s-(%s)\n" -msgstr "GNU %s, verzió %s\n" +msgstr "GNU bash, %s-(%s) verzió\n" #: shell.c:1794 #, c-format @@ -1476,491 +1507,464 @@ msgid "" "Usage:\t%s [GNU long option] [option] ...\n" "\t%s [GNU long option] [option] script-file ...\n" msgstr "" -"Használat:\t%s [GNU hosszú opció] [opció] ...\n" -"\t%s [GNU hosszú opció] [opció] parancs fájl ...\n" +"Használat: %s [GNU hosszú kapcsoló] [kapcsoló] ...\n" +" %s [GNU hosszú kapcsoló] [kapcsoló] parancsfájl ...\n" #: shell.c:1796 msgid "GNU long options:\n" -msgstr "GNU hosszú opciók:\n" +msgstr "GNU hosszú kapcsolók:\n" #: shell.c:1800 msgid "Shell options:\n" -msgstr "Parancsértelmezõ opciók:\n" +msgstr "ParancsértelmezÅ‘-kapcsolók:\n" #: shell.c:1801 -#, fuzzy msgid "\t-irsD or -c command or -O shopt_option\t\t(invocation only)\n" -msgstr "\t-irsD vagy -c parancs\t\t(csak végrehajtható)\n" +msgstr "\t-irsD vagy -c parancs vagy -O shopt_option\t\t(csak hÃvás)\n" #: shell.c:1816 #, c-format msgid "\t-%s or -o option\n" -msgstr "\t-%s vagy -o opciók\n" +msgstr "\t-%s vagy -o kapcsoló\n" #: shell.c:1822 #, c-format msgid "Type `%s -c \"help set\"' for more information about shell options.\n" -msgstr "" -"Írja be a `%s -c \"help set\"' ha több információra van szüksége a " -"parancsértelmezõ opcióival kapcsolatban.\n" +msgstr "A „%s -c \"help set\"†további információt ad a parancsértelmezÅ‘-beállÃtásokról.\n" #: shell.c:1823 #, c-format msgid "Type `%s -c help' for more information about shell builtin commands.\n" -msgstr "" -"Írja be a `%s -c \"help set\"' ha több információra van szüksége a " -"parancsértelmezõ beépített utasításaival kapcsolatban.\n" +msgstr "A „%s -c help†további információt ad a beépÃtett parancsokról.\n" #: shell.c:1824 #, c-format msgid "Use the `bashbug' command to report bugs.\n" -msgstr "" +msgstr "A „bashbug†paranccsal jelenthet hibákat.\n" #: sig.c:626 #, c-format msgid "sigprocmask: %d: invalid operation" -msgstr "" +msgstr "sigprocmask: %d: érvénytelen művelet" #: siglist.c:48 msgid "Bogus signal" -msgstr "Hamis jel" +msgstr "Hibás szignál" #: siglist.c:51 msgid "Hangup" -msgstr "Bontás" +msgstr "Bontás" #: siglist.c:55 msgid "Interrupt" -msgstr "Megszakítás" +msgstr "MegszakÃtás" #: siglist.c:59 msgid "Quit" -msgstr "Kilépés" +msgstr "Abbahagyás" #: siglist.c:63 msgid "Illegal instruction" -msgstr "Érvénytelen utasítás" +msgstr "Érvénytelen utasÃtás" #: siglist.c:67 msgid "BPT trace/trap" -msgstr "BPT nyomkövetés/csapda" +msgstr "BPT trace/trap" #: siglist.c:75 msgid "ABORT instruction" -msgstr "Utasítás MEGSZAKÍTÁSA" +msgstr "ABORT utasÃtás" #: siglist.c:79 msgid "EMT instruction" -msgstr "EMT utasítás" +msgstr "EMT utasÃtás" #: siglist.c:83 msgid "Floating point exception" -msgstr "Lebegõ pontos esemény" +msgstr "LebegÅ‘pontos kivétel" #: siglist.c:87 msgid "Killed" -msgstr "Kilõve" +msgstr "KilÅ‘ve" #: siglist.c:91 msgid "Bus error" -msgstr "Busz hiba" +msgstr "Buszhiba" #: siglist.c:95 msgid "Segmentation fault" -msgstr "Szegmens hiba" +msgstr "Szegmenshiba" #: siglist.c:99 msgid "Bad system call" -msgstr "Rossz rendszerhívás" +msgstr "Rossz rendszerhÃvás" #: siglist.c:103 msgid "Broken pipe" -msgstr "Törött csõ (pipe)" +msgstr "Törött csÅ‘vezeték" #: siglist.c:107 msgid "Alarm clock" -msgstr "Emlékeztetés" +msgstr "Ébresztés" #: siglist.c:111 -#, fuzzy msgid "Terminated" -msgstr "ki." +msgstr "Befejezve" #: siglist.c:115 msgid "Urgent IO condition" -msgstr "Sürgõs IO feltétel" +msgstr "SürgÅ‘s IO körülmény" #: siglist.c:119 msgid "Stopped (signal)" -msgstr "Megállítva (jellel)" +msgstr "MegállÃtva (szignál)" #: siglist.c:127 msgid "Continue" -msgstr "Folytatás" +msgstr "Folytatás" #: siglist.c:135 msgid "Child death or stop" -msgstr "Gyermek objektum meghalt vagy megállt" +msgstr "Gyermek halála vagy megállÃtása" #: siglist.c:139 msgid "Stopped (tty input)" -msgstr "Megállítva (tty bemenet)" +msgstr "MegállÃtva (konzolbemenet)" #: siglist.c:143 msgid "Stopped (tty output)" -msgstr "Megállítva (tty kimenet)" +msgstr "MegállÃtva (konzolkimenet)" #: siglist.c:147 msgid "I/O ready" -msgstr "I/O kész" +msgstr "IO kész" #: siglist.c:151 msgid "CPU limit" -msgstr "CPU határ" +msgstr "CPU-korlátozás" #: siglist.c:155 msgid "File limit" -msgstr "Fájl határ" +msgstr "Fájlkorlátozás" #: siglist.c:159 msgid "Alarm (virtual)" -msgstr "Emlékeztetés (virtuális)" +msgstr "Ébresztés (virtuális)" #: siglist.c:163 msgid "Alarm (profile)" -msgstr "Emlékeztetés (profil)" +msgstr "Ébresztés (profilozás)" #: siglist.c:167 msgid "Window changed" -msgstr "Ablak megváltozott" +msgstr "Ablak változott" #: siglist.c:171 msgid "Record lock" -msgstr "Felvétel zárolva" +msgstr "Elveszett zárolás" #: siglist.c:175 msgid "User signal 1" -msgstr "Felhasználói jel 1" +msgstr "Felhasználói 1." #: siglist.c:179 msgid "User signal 2" -msgstr "Felhasználói jel 2" +msgstr "Felhasználói 2." #: siglist.c:183 msgid "HFT input data pending" -msgstr "HFT bemeneti adat függõben" +msgstr "HFT bemeneti adat vár" #: siglist.c:187 msgid "power failure imminent" -msgstr "áram kimaradás várható" +msgstr "táphiba fenyeget" #: siglist.c:191 msgid "system crash imminent" -msgstr "rendszer sérülés várható" +msgstr "rendszerleállás fenyeget" #: siglist.c:195 msgid "migrate process to another CPU" -msgstr "feladat átadása másik CPU-nak" +msgstr "folyamat átvitele másik CPU-ra" #: siglist.c:199 msgid "programming error" -msgstr "programozási hiba" +msgstr "programozási hiba" #: siglist.c:203 msgid "HFT monitor mode granted" -msgstr "HFT felügyelõ mód engedélyezve" +msgstr "HFT monitor mód megadva" #: siglist.c:207 msgid "HFT monitor mode retracted" -msgstr "HFT felügyelõ mód visszavonva" +msgstr "HFT monitor mód visszavonva" #: siglist.c:211 msgid "HFT sound sequence has completed" -msgstr "HFT hangsorozat behelyezve" +msgstr "HFT hangfolyamat befejezve" #: siglist.c:215 msgid "Information request" -msgstr "" +msgstr "Információkérés" #: siglist.c:223 msgid "Unknown Signal #" -msgstr "Ismeretlen # Szignál" +msgstr "Ismeretlen szignál #" #: siglist.c:225 #, c-format msgid "Unknown Signal #%d" -msgstr "Ismeretlen #%d Szignál" +msgstr "%d. számú ismeretlen szignál" #: subst.c:1333 subst.c:1454 -#, fuzzy, c-format +#, c-format msgid "bad substitution: no closing `%s' in %s" -msgstr "rossz behelyettesítés: ne a %s be a %s-t" +msgstr "hibás helyettesÃtés: nincs záró „%s†a következÅ‘ben: %s" #: subst.c:2735 #, c-format msgid "%s: cannot assign list to array member" -msgstr "%s: nem lehet a listához rendelni az elemet" +msgstr "%s: lista nem adható tömbelemnek értékül" #: subst.c:4754 subst.c:4770 -#, fuzzy msgid "cannot make pipe for process substitution" -msgstr "nem lehet létrehozni a pipe-ot feladat behelyettesítéshez: %s" +msgstr "nem hozható létre a csÅ‘vezeték a folyamatbehelyettesÃtéshez" #: subst.c:4802 -#, fuzzy msgid "cannot make child for process substitution" -msgstr "" -"nem lehet létrehozni a gyermekfolyamatott feladat behelyettesítéshez: %s" +msgstr "nem hozható létre a gyermek a folyamatbehelyettesÃtéshez" #: subst.c:4847 -#, fuzzy, c-format +#, c-format msgid "cannot open named pipe %s for reading" -msgstr "nem lehet létrehozni a %s \"named pipe\"-ot a %s-nek: %s" +msgstr "nem nyitható meg olvasásra a(z) %s csÅ‘vezeték" #: subst.c:4849 -#, fuzzy, c-format +#, c-format msgid "cannot open named pipe %s for writing" -msgstr "nem lehet létrehozni a %s \"named pipe\"-ot a %s-nek: %s" +msgstr "nem nyitható meg Ãrásra a(z) %s csÅ‘vezeték" #: subst.c:4867 -#, fuzzy, c-format +#, c-format msgid "cannot duplicate named pipe %s as fd %d" -msgstr "nem lehet másolni a %s \"named pipe\"-ot mint fd %d :%s" +msgstr "nem duplikálható a(z) %s csÅ‘vezeték %d. fájlleÃróként" #: subst.c:5063 -#, fuzzy msgid "cannot make pipe for command substitution" -msgstr "nem lehet létrehozni a \"pipe\"-ot parancs behelyettesítéséhez: %s" +msgstr "nem hozható létre csÅ‘vezeték a parancsbehelyettesÃtéshez" #: subst.c:5097 -#, fuzzy msgid "cannot make child for command substitution" -msgstr "" -"nem lehet létrehozni a gyermekfolyamatot a parancs behelyettesítéséhez: %s" +msgstr "nem hozható létre gyermek a parancsbehelyettesÃtéshez" #: subst.c:5114 -#, fuzzy msgid "command_substitute: cannot duplicate pipe as fd 1" -msgstr "command_substitute: nem lehet másolni a \"pipe\"-ot mint fd 1: %s" +msgstr "command_substitute: nem duplikálható a csÅ‘vezeték 1. fájlleÃróként" #: subst.c:5617 #, c-format msgid "%s: parameter null or not set" -msgstr "%s paraméter semmis vagy nincs beállítva" +msgstr "%s: a paraméter null vagy nincs beállÃtva" #: subst.c:5907 #, c-format msgid "%s: substring expression < 0" -msgstr "%s szövegrész kifejezés < 0" +msgstr "%s: részkarakterlánc-kifejezés < 0" #: subst.c:6965 #, c-format msgid "%s: bad substitution" -msgstr "%s rossz behelyettesítés" +msgstr "%s: rossz helyettesÃtés" #: subst.c:7045 #, c-format msgid "$%s: cannot assign in this way" -msgstr "$%s így nem lehet hozzárendelni" +msgstr "$%s: nem lehet Ãgy értéket adni" #: subst.c:7374 -msgid "" -"future versions of the shell will force evaluation as an arithmetic " -"substitution" -msgstr "" +msgid "future versions of the shell will force evaluation as an arithmetic substitution" +msgstr "a parancsértelmezÅ‘ késÅ‘bbi verziói kötelezÅ‘vé teszik majd az aritmetikai kiértékelést" #: subst.c:7839 -#, fuzzy, c-format +#, c-format msgid "bad substitution: no closing \"`\" in %s" -msgstr "rossz behelyettesítés: ne a %s be a %s-t" +msgstr "hibás helyettesÃtés: nincs záró „`†a következÅ‘ben: %s" #: subst.c:8720 #, c-format msgid "no match: %s" -msgstr "" +msgstr "nincs találat: %s" #: test.c:146 msgid "argument expected" -msgstr "paraméter szükséges" +msgstr "argumentum szükséges" #: test.c:155 #, c-format msgid "%s: integer expression expected" -msgstr "%s egész szám szükséges" +msgstr "%s: egész kifejezés szükséges" #: test.c:263 msgid "`)' expected" -msgstr "')' szükséges" +msgstr "„)†szükséges" #: test.c:265 #, c-format msgid "`)' expected, found %s" -msgstr "')' szükséges, %s-t találtam" +msgstr "„)†szükséges %s helyett" #: test.c:280 test.c:693 test.c:696 #, c-format msgid "%s: unary operator expected" -msgstr "%s unáris mûvelet szükséges" +msgstr "%s: egyoperandusú operátor szükséges" #: test.c:449 test.c:736 #, c-format msgid "%s: binary operator expected" -msgstr "%s:bináris mûvelet szükséges" +msgstr "%s: kétoperandusú operátor szükséges" #: test.c:811 msgid "missing `]'" -msgstr "hiányzó ']'" +msgstr "hiányzó „]â€" #: trap.c:203 -#, fuzzy msgid "invalid signal number" -msgstr "rossz jel(signal) szám" +msgstr "érvénytelen szignálszám" #: trap.c:327 #, c-format msgid "run_pending_traps: bad value in trap_list[%d]: %p" -msgstr "" +msgstr "run_pending_traps: rossz érték a trap_list[%d]-ban: %p" #: trap.c:331 #, c-format -msgid "" -"run_pending_traps: signal handler is SIG_DFL, resending %d (%s) to myself" -msgstr "" +msgid "run_pending_traps: signal handler is SIG_DFL, resending %d (%s) to myself" +msgstr "run_pending_traps: szignálkezelÅ‘ a SIG_DFL, %d (%s) újraküldése önmagunknak" #: trap.c:380 -#, fuzzy, c-format +#, c-format msgid "trap_handler: bad signal %d" -msgstr "trap_handler: Rossz jel(signal) %d" +msgstr "trap_handler: rossz szignál: %d" #: variables.c:363 #, c-format msgid "error importing function definition for `%s'" -msgstr "hiba a %s funkció definíció importálásakor" +msgstr "hiba a függvénydefinÃció betöltésekor: „%sâ€" #: variables.c:748 #, c-format msgid "shell level (%d) too high, resetting to 1" -msgstr "" +msgstr "a parancsértelmezÅ‘ szintje (%d) túl magas, visszaállÃtás 1-re" #: variables.c:1915 msgid "make_local_variable: no function context at current scope" -msgstr "" +msgstr "make_local_variable: nincs függvénykörnyezet az aktuális látókörben" #: variables.c:3159 msgid "all_local_variables: no function context at current scope" -msgstr "" +msgstr "all_local_variables: nincs függvénykörnyezet az aktuális látókörben" #: variables.c:3376 -#, fuzzy, c-format +#, c-format msgid "%s has null exportstr" -msgstr "%s paraméter semmis vagy nincs beállítva" +msgstr "%s exportstr-je null" #: variables.c:3381 variables.c:3390 #, c-format msgid "invalid character %d in exportstr for %s" -msgstr "" +msgstr "érvénytelen karakter (%d) %s exportstr-jében" #: variables.c:3396 #, c-format msgid "no `=' in exportstr for %s" -msgstr "" +msgstr "nincs „=†%s exportstr-jében" #: variables.c:3835 msgid "pop_var_context: head of shell_variables not a function context" -msgstr "" +msgstr "pop_var_context: shell_variables feje nem egy függvénykörnyezet" #: variables.c:3848 msgid "pop_var_context: no global_variables context" -msgstr "" +msgstr "pop_var_context: nincs global_variables környezet" #: variables.c:3922 msgid "pop_scope: head of shell_variables not a temporary environment scope" -msgstr "" +msgstr "pop_scope: shell_variables feje nem egy átmeneti környezeti látókör" #: variables.c:4678 -#, fuzzy, c-format +#, c-format msgid "%s: %s: cannot open as FILE" -msgstr "%s: nem lehet létrehozni: %s" +msgstr "%s: %s: nem nyitható meg FILE-ként" #: variables.c:4683 #, c-format msgid "%s: %s: invalid value for trace file descriptor" -msgstr "" +msgstr "%s: %s: érvénytelen érték a trace fájlleÃróhoz" #: version.c:46 msgid "Copyright (C) 2009 Free Software Foundation, Inc." -msgstr "" +msgstr "Copyright © 2009 Free Software Foundation, Inc." #: version.c:47 -msgid "" -"License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl." -"html>\n" -msgstr "" +msgid "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n" +msgstr "A licenc GPLv3+: a GNU GPL 3. vagy újabb változata <http://gnu.org/licenses/gpl.html>\n" #: version.c:86 version2.c:83 -#, fuzzy, c-format +#, c-format msgid "GNU bash, version %s (%s)\n" -msgstr "GNU %s, verzió %s\n" +msgstr "GNU bash, %s (%s) verzió\n" #: version.c:91 version2.c:88 #, c-format msgid "This is free software; you are free to change and redistribute it.\n" -msgstr "" +msgstr "Ez egy szabad szoftver, terjesztheti és/vagy módosÃthatja.\n" #: version.c:92 version2.c:89 #, c-format msgid "There is NO WARRANTY, to the extent permitted by law.\n" -msgstr "" +msgstr "NINCS GARANCIA, a törvény által engedélyezett mértékig.\n" #: version2.c:86 #, c-format msgid "Copyright (C) 2009 Free Software Foundation, Inc.\n" -msgstr "" +msgstr "Copyright © 2009 Free Software Foundation, Inc.\n" #: version2.c:87 #, c-format -msgid "" -"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl." -"html>\n" -msgstr "" +msgid "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n" +msgstr "A licenc GPLv2+: a GNU GPL 2. vagy újabb változata <http://gnu.org/licenses/gpl.html>\n" #: xmalloc.c:91 -#, fuzzy, c-format +#, c-format msgid "%s: cannot allocate %lu bytes (%lu bytes allocated)" -msgstr "xmalloc: nem lehet újra lefoglalni %lu bájtot (%lu bájt lefoglalva)" +msgstr "%s: nem lehetséges %lu byte foglalása (%lu byte lett foglalva)" #: xmalloc.c:93 -#, fuzzy, c-format +#, c-format msgid "%s: cannot allocate %lu bytes" -msgstr "xmalloc: nem lehet újra lefoglalni %lu bájtot (%lu bájt lefoglalva)" +msgstr "%s: nem lehetséges %lu byte foglalása" #: xmalloc.c:163 -#, fuzzy, c-format +#, c-format msgid "%s: %s:%d: cannot allocate %lu bytes (%lu bytes allocated)" -msgstr "xmalloc: nem lehet újra lefoglalni %lu bájtot (%lu bájt lefoglalva)" +msgstr "%s: %s:%d nem lehetséges %lu byte foglalása (%lu byte lett foglalva)" #: xmalloc.c:165 -#, fuzzy, c-format +#, c-format msgid "%s: %s:%d: cannot allocate %lu bytes" -msgstr "xmalloc: nem lehet újra lefoglalni %lu bájtot (%lu bájt lefoglalva)" +msgstr "%s: %s:%d nem lehetséges %lu byte foglalása" #: builtins.c:43 msgid "alias [-p] [name[=value] ... ]" -msgstr "alias [-p] [név[=érték] ... ]" +msgstr "alias [-p] [név[=érték] ... ]" #: builtins.c:47 -#, fuzzy msgid "unalias [-a] name [name ...]" -msgstr "unalias [-a] [név ...]" +msgstr "unalias [-a] név [név ...]" #: builtins.c:51 -#, fuzzy -msgid "" -"bind [-lpvsPVS] [-m keymap] [-f filename] [-q name] [-u name] [-r keyseq] [-" -"x keyseq:shell-command] [keyseq:readline-function or readline-command]" -msgstr "" -"bind [-lpvsPVS] [-m keymap] [-f fájlnév] [-q név] [-r bill.kód] [bill.kód:" -"readline-funkció]" +msgid "bind [-lpvsPVS] [-m keymap] [-f filename] [-q name] [-u name] [-r keyseq] [-x keyseq:shell-command] [keyseq:readline-function or readline-command]" +msgstr "bind [-lpvsPVS] [-m kiosztás] [-f fájlnév] [-q név] [-u név] [-r billkomb] [-x billkomb:shell-parancs] [billkomb:readline-függvény vagy readline-parancs]" #: builtins.c:54 msgid "break [n]" @@ -1975,19 +1979,16 @@ msgid "builtin [shell-builtin [arg ...]]" msgstr "builtin [shell-builtin [arg ...]]" #: builtins.c:61 -#, fuzzy msgid "caller [expr]" -msgstr "test [kifelyezés]" +msgstr "caller [kif]" #: builtins.c:64 -#, fuzzy msgid "cd [-L|-P] [dir]" -msgstr "cd [-PL] [könyvtár]" +msgstr "cd [-L|-P] [ktár]" #: builtins.c:66 -#, fuzzy msgid "pwd [-LP]" -msgstr "pwd [-PL]" +msgstr "pwd [-LP]" #: builtins.c:68 msgid ":" @@ -1995,30 +1996,27 @@ msgstr ":" #: builtins.c:70 msgid "true" -msgstr "" +msgstr "true" #: builtins.c:72 msgid "false" -msgstr "" +msgstr "false" #: builtins.c:74 msgid "command [-pVv] command [arg ...]" -msgstr "command [-pVv] parancs [arg ...]" +msgstr "command [-pVv] parancs [arg ...]" #: builtins.c:76 -#, fuzzy msgid "declare [-aAfFilrtux] [-p] [name[=value] ...]" -msgstr "declare [-afFrxi] [-p] név[=érték] ..." +msgstr "declare [-aAfFilrtux] [-p] [név[=érték] ...]" #: builtins.c:78 -#, fuzzy msgid "typeset [-aAfFilrtux] [-p] name[=value] ..." -msgstr "typeset [-afFrxi] [-p] név[=érték] ..." +msgstr "typeset [-aAfFilrtux] [-p] név[=érték] ..." #: builtins.c:80 -#, fuzzy msgid "local [option] name[=value] ..." -msgstr "local name[=érték] ..." +msgstr "local [kapcsoló] név[=érték] ..." #: builtins.c:83 msgid "echo [-neE] [arg ...]" @@ -2029,9 +2027,8 @@ msgid "echo [-n] [arg ...]" msgstr "echo [-n] [arg ...]" #: builtins.c:90 -#, fuzzy msgid "enable [-a] [-dnps] [-f filename] [name ...]" -msgstr "enable [-pnds] [-a] [-f fájlnév] [név ...]" +msgstr "enable [-a] [-dnps] [-f fájlnév] [név ...]" #: builtins.c:92 msgid "eval [arg ...]" @@ -2039,119 +2036,95 @@ msgstr "eval [arg ...]" #: builtins.c:94 msgid "getopts optstring name [arg]" -msgstr "getopts optstring név [arg]" +msgstr "getopts opciók név [arg]" #: builtins.c:96 -#, fuzzy msgid "exec [-cl] [-a name] [command [arguments ...]] [redirection ...]" -msgstr "exec [-cl] [-a név] fájl [átirányítás ...]" +msgstr "exec [-cl] [-a név] [parancs [argumentumok ...]] [átirányÃtás ...]" #: builtins.c:98 msgid "exit [n]" msgstr "exit [n]" #: builtins.c:100 -#, fuzzy msgid "logout [n]" -msgstr "logout" +msgstr "logout [n]" #: builtins.c:103 -#, fuzzy msgid "fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command]" -msgstr "fc [-e ename] [-nlr] [elsõ] [utolsó] vagy fc -s [pat=rep] [cmd]" +msgstr "fc [-e ename] [-lnr] [elsÅ‘] [utolsó] vagy fc -s [minta=csere] [parancs]" #: builtins.c:107 msgid "fg [job_spec]" -msgstr "fg [munka_folyamat]" +msgstr "fg [munkaszám]" #: builtins.c:111 -#, fuzzy msgid "bg [job_spec ...]" -msgstr "bg [munka_folyamat]" +msgstr "bg [munkaszám ...]" #: builtins.c:114 -#, fuzzy msgid "hash [-lr] [-p pathname] [-dt] [name ...]" -msgstr "hash [-r] [-p útvonal] [név ...]" +msgstr "hash [-lr] [-p útvonal] [-dt] [név ...]" #: builtins.c:117 -#, fuzzy msgid "help [-dms] [pattern ...]" -msgstr "help [minta ...]" +msgstr "help [-dms] [minta ...]" #: builtins.c:121 -#, fuzzy -msgid "" -"history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg " -"[arg...]" -msgstr "" -"history [-c] [n] vagy history -awrn [fájlnév] vagy history -ps arg [arg...]" +msgid "history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg [arg...]" +msgstr "history [-c] [-d szám] [n] vagy history -anrw [fájlnév] vagy history -ps arg [arg...]" #: builtins.c:125 msgid "jobs [-lnprs] [jobspec ...] or jobs -x command [args]" -msgstr "jobs [-lnprs] [munkafolyamat ...] vagy jobs -x parancs [args]" +msgstr "jobs [-lnprs] [munkaszám ...] vagy jobs -x parancs [args]" #: builtins.c:129 -#, fuzzy msgid "disown [-h] [-ar] [jobspec ...]" -msgstr "disown [munka folyamat ...]" +msgstr "disown [-h] [-ar] [munkaszám ...]" #: builtins.c:132 -#, fuzzy -msgid "" -"kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l " -"[sigspec]" -msgstr "" -"kill [-s jeltípus(sigspec) | -n jel(signum) | -jeltípus(sigspec)] [pid | " -"job]... vagy kill -l [jeltípus(sigspec)]" +msgid "kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]" +msgstr "kill [-s szignál | -n szignálszám | -szignál] pid | munkaszám ... vagy kill -l [szignál]" #: builtins.c:134 msgid "let arg [arg ...]" msgstr "let arg [arg ...]" #: builtins.c:136 -msgid "" -"read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p " -"prompt] [-t timeout] [-u fd] [name ...]" -msgstr "" +msgid "read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]" +msgstr "read [-ers] [-a tömb] [-d elválasztó] [-i szöveg] [-n nchars] [-N nchars] [-p prompt] [-t idÅ‘keret] [-u fd] [név ...]" #: builtins.c:138 msgid "return [n]" msgstr "return [n]" #: builtins.c:140 -#, fuzzy msgid "set [--abefhkmnptuvxBCHP] [-o option-name] [arg ...]" -msgstr "set [--abefhkmnptuvxBCHP] [-o opció] [arg ...]" +msgstr "set [--abefhkmnptuvxBCHP] [-o beállÃtásnév] [arg ...]" #: builtins.c:142 msgid "unset [-f] [-v] [name ...]" -msgstr "unset [-f] [-v] [név ...]" +msgstr "unset [-f] [-v] [név ...]" #: builtins.c:144 -#, fuzzy msgid "export [-fn] [name[=value] ...] or export -p" -msgstr "export [-nf] [név ...] vagy export -p" +msgstr "export [-fn] [név[=érték] ...] vagy export -p" #: builtins.c:146 -#, fuzzy msgid "readonly [-af] [name[=value] ...] or readonly -p" -msgstr "readonly [-anf] [név ...] vagy readonly -p" +msgstr "readonly [-af] [név[=érték] ...] vagy readonly -p" #: builtins.c:148 -#, fuzzy msgid "shift [n]" -msgstr "exit [n]" +msgstr "shift [n]" #: builtins.c:150 -#, fuzzy msgid "source filename [arguments]" -msgstr "forrás fájlneve" +msgstr "source fájlnév [argumentumok]" #: builtins.c:152 -#, fuzzy msgid ". filename [arguments]" -msgstr "fájlnév" +msgstr ". fájlnév [argumentumok]" #: builtins.c:155 msgid "suspend [-f]" @@ -2159,7 +2132,7 @@ msgstr "suspend [-f]" #: builtins.c:158 msgid "test [expr]" -msgstr "test [kifelyezés]" +msgstr "test [expr]" #: builtins.c:160 msgid "[ arg... ]" @@ -2170,64 +2143,52 @@ msgid "times" msgstr "times" #: builtins.c:164 -#, fuzzy msgid "trap [-lp] [[arg] signal_spec ...]" -msgstr "trap [arg] [signal_spec] vagy trap -l" +msgstr "trap [-lp] [[arg] szignál ...]" #: builtins.c:166 -#, fuzzy msgid "type [-afptP] name [name ...]" -msgstr "type [-apt] név [név ...]" +msgstr "type [-afptP] név [név ...]" #: builtins.c:169 -#, fuzzy msgid "ulimit [-SHacdefilmnpqrstuvx] [limit]" -msgstr "ulimit [-SHacdfmstpnuv] [határ]" +msgstr "ulimit [-SHacdefilmnpqrstuvx] [korlát]" #: builtins.c:172 -#, fuzzy msgid "umask [-p] [-S] [mode]" -msgstr "mask [-S] [mód]" +msgstr "umask [-p] [-S] [mód]" #: builtins.c:175 -#, fuzzy msgid "wait [id]" -msgstr "wait [n]" +msgstr "wait [id]" #: builtins.c:179 -#, fuzzy msgid "wait [pid]" -msgstr "wait [n]" +msgstr "wait [pid]" #: builtins.c:182 -#, fuzzy msgid "for NAME [in WORDS ... ] ; do COMMANDS; done" -msgstr "for NÉV [in SZAVAK ... ;] do PARANCSOK; done" +msgstr "for NÉV [in SZAVAK ... ] ; do PARANCSOK; done" #: builtins.c:184 -#, fuzzy msgid "for (( exp1; exp2; exp3 )); do COMMANDS; done" -msgstr "for NÉV [in SZAVAK ... ;] do PARANCSOK; done" +msgstr "for (( kif1; kif2; kif3 )); do PARANCSOK; done" #: builtins.c:186 msgid "select NAME [in WORDS ... ;] do COMMANDS; done" -msgstr "select NÉV [in SZAVAK ... ;] do PARANCSOK; done" +msgstr "select NÉV [in SZAVAK ... ;] do PARANCSOK; done" #: builtins.c:188 msgid "time [-p] pipeline" -msgstr "" +msgstr "time [-p] csÅ‘vezeték" #: builtins.c:190 msgid "case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac" -msgstr "case SZÓ in [MINTA [| MINTA]...) PARANCSOK ;;]... esac" +msgstr "case SZÓ in [MINTA [| MINTA]...) PARANCSOK ;;]... esac" #: builtins.c:192 -msgid "" -"if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else " -"COMMANDS; ] fi" -msgstr "" -"if PARANCSOK; then PARANCSOK; [ elif PARANCSOK; then PARANCSOK; ]... [ else " -"PARANCSOK; ] fi" +msgid "if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi" +msgstr "if PARANCSOK; then PARANCSOK; [ elif PARANCSOK; then PARANCSOK; ]... [ else PARANCSOK; ] fi" #: builtins.c:194 msgid "while COMMANDS; do COMMANDS; done" @@ -2239,91 +2200,71 @@ msgstr "until PARANCSOK; do PARANCSOK; done" #: builtins.c:198 msgid "coproc [NAME] command [redirections]" -msgstr "" +msgstr "coproc [NÉV] parancs [átirányÃtások]" #: builtins.c:200 -#, fuzzy msgid "function name { COMMANDS ; } or name () { COMMANDS ; }" -msgstr "function NÉV { PARANCSOK ; } vagy NÉV () { PARANCSOK ; }" +msgstr "function név { PARANCSOK ; } vagy név () { PARANCSOK ; }" #: builtins.c:202 -#, fuzzy msgid "{ COMMANDS ; }" -msgstr "{ PARANCSOK }" +msgstr "{ PARANCSOK ; }" #: builtins.c:204 -#, fuzzy msgid "job_spec [&]" -msgstr "fg [munka_folyamat]" +msgstr "munkaszám [&]" #: builtins.c:206 -#, fuzzy msgid "(( expression ))" -msgstr "várható kifejezés" +msgstr "(( kifejezés ))" #: builtins.c:208 -#, fuzzy msgid "[[ expression ]]" -msgstr "várható kifejezés" +msgstr "[[ kifejezés ]]" #: builtins.c:210 -#, fuzzy msgid "variables - Names and meanings of some shell variables" -msgstr "" -"A parancsértelmezõ változói felhasználhatók operandusként. A változó neve" +msgstr "variables - Néhány változó neve és jelentése" #: builtins.c:213 -#, fuzzy msgid "pushd [-n] [+N | -N | dir]" -msgstr "pushd [könyvtár | +N | -N] [-n]" +msgstr "pushd [-n] [+N | -N | ktár]" #: builtins.c:217 -#, fuzzy msgid "popd [-n] [+N | -N]" -msgstr "popd [+N | -N] [-n]" +msgstr "popd [-n] [+N | -N]" #: builtins.c:221 msgid "dirs [-clpv] [+N] [-N]" msgstr "dirs [-clpv] [+N] [-N]" #: builtins.c:224 -#, fuzzy msgid "shopt [-pqsu] [-o] [optname ...]" -msgstr "shopt [-pqsu] [-o hosszú opciók] optnév [optnév...]" +msgstr "shopt [-pqsu] [-o] [optnév ...]" #: builtins.c:226 msgid "printf [-v var] format [arguments]" -msgstr "" +msgstr "printf [-v változó] formátum [argumentumok]" #: builtins.c:229 -msgid "" -"complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-" -"W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S " -"suffix] [name ...]" -msgstr "" +msgid "complete [-abcdefgjksuv] [-pr] [-DE] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [name ...]" +msgstr "complete [-abcdefgjksuv] [-pr] [-DE] [-o beállÃtás] [-A művelet] [-G globminta] [-W szólista] [-F függvény] [-C parancs] [-X szűrÅ‘minta] [-P prefixum] [-S szuffixum] [név ...]" #: builtins.c:233 -msgid "" -"compgen [-abcdefgjksuv] [-o option] [-A action] [-G globpat] [-W wordlist] " -"[-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [word]" -msgstr "" +msgid "compgen [-abcdefgjksuv] [-o option] [-A action] [-G globpat] [-W wordlist] [-F function] [-C command] [-X filterpat] [-P prefix] [-S suffix] [word]" +msgstr "compgen [-abcdefgjksuv] [-o beállÃtás] [-A művelet] [-G globminta] [-W szólista] [-F függvény] [-C parancs] [-X szűrÅ‘minta] [-P prefixum] [-S szuffixum] [szó]" #: builtins.c:237 -#, fuzzy msgid "compopt [-o|+o option] [-DE] [name ...]" -msgstr "type [-apt] név [név ...]" +msgstr "compopt [-o|+o beállÃtás] [-DE] [név ...]" #: builtins.c:240 -msgid "" -"mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c " -"quantum] [array]" -msgstr "" +msgid "mapfile [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]" +msgstr "mapfile [-n szám] [-O kezdet] [-s szám] [-t] [-u fd] [-C parancs] [-c távolság] [tömb]" #: builtins.c:242 -msgid "" -"readarray [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c " -"quantum] [array]" -msgstr "" +msgid "readarray [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]" +msgstr "readarray [-n szám] [-O kezdet] [-s szám] [-t] [-u fd] [-C parancs] [-c távolság] [tömb]" #: builtins.c:254 msgid "" @@ -2340,13 +2281,25 @@ msgid "" " -p\tPrint all defined aliases in a reusable format\n" " \n" " Exit Status:\n" -" alias returns true unless a NAME is supplied for which no alias has " -"been\n" +" alias returns true unless a NAME is supplied for which no alias has been\n" " defined." msgstr "" +"Aliasok definiálása vagy kiÃrása.\n" +" \n" +" Argumentumok nélkül az „alias†kiÃr egy újrahasználható listát a meglé-\n" +" vÅ‘ aliasokról „alias NÉV=ÉRTÉK' formában a szabványos kimenetre.\n" +" \n" +" Különben egy NÉV nevű aliast definiál ÉRTÉK értékkel. Az ÉRTÉK végén a\n" +" záró szóköz lehetÅ‘vé teszi a következÅ‘ szó számára is az aliashelyette-\n" +" sÃtést.\n" +" \n" +" BeállÃtások:\n" +" -p KiÃr minden aliast a fenti formában\n" +" \n" +" Kilépési kód:\n" +" igazzal tér vissza, kivéve ha nincs megadott NÉV nevű alias definiálva." #: builtins.c:276 -#, fuzzy msgid "" "Remove each NAME from the list of defined aliases.\n" " \n" @@ -2354,7 +2307,13 @@ msgid "" " -a\tremove all alias definitions.\n" " \n" " Return success unless a NAME is not an existing alias." -msgstr "NÉV eltávolítása a meghatározott aliasok listájából. A -a opció" +msgstr "" +"Minden NÉV eltávolÃtása a definiált aliasok közül.\n" +" \n" +" BeállÃtások:\n" +" -a minden definÃció törlése.\n" +" \n" +" Sikeresen tér vissza, kivéve ha nincs megadott NÉV nevű alias." #: builtins.c:289 msgid "" @@ -2368,24 +2327,20 @@ msgid "" " Options:\n" " -m keymap Use KEYMAP as the keymap for the duration of this\n" " command. Acceptable keymap names are emacs,\n" -" emacs-standard, emacs-meta, emacs-ctlx, vi, vi-" -"move,\n" +" emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move,\n" " vi-command, and vi-insert.\n" " -l List names of functions.\n" " -P List function names and bindings.\n" " -p List functions and bindings in a form that can be\n" " reused as input.\n" -" -S List key sequences that invoke macros and their " -"values\n" -" -s List key sequences that invoke macros and their " -"values\n" +" -S List key sequences that invoke macros and their values\n" +" -s List key sequences that invoke macros and their values\n" " in a form that can be reused as input.\n" " -V List variable names and values\n" " -v List variable names and values in a form that can\n" " be reused as input.\n" " -q function-name Query about which keys invoke the named function.\n" -" -u function-name Unbind all keys which are bound to the named " -"function.\n" +" -u function-name Unbind all keys which are bound to the named function.\n" " -r keyseq Remove the binding for KEYSEQ.\n" " -f filename Read key bindings from FILENAME.\n" " -x keyseq:shell-command\tCause SHELL-COMMAND to be executed when\n" @@ -2394,9 +2349,40 @@ msgid "" " Exit Status:\n" " bind returns 0 unless an unrecognized option is given or an error occurs." msgstr "" +"Readline billentyűkötések és változók beállÃtása.\n" +" \n" +" Egy billentyűsorozat hozzárendelése Readline függvényhez vagy makróhoz,\n" +" vagy Readline változó beállÃtása. A beállÃtás nélküli szintaxis mege-\n" +" gyezik az ~/.inputrc-ben találhatóval, de kell legyen egy argumentuma:\n" +" pl. bind '\"\\C-x\\C-r\": re-read-init-file'.\n" +" \n" +" BeállÃtások:\n" +" -m kiosztás KIOSZTÃS használata kiosztásként a parancs hatásá-\n" +" nak idejére. Elfogadható kiosztásnevek: emacs,\n" +" emacs-standard, emacs-meta, emacs-ctlx, vi, vi-\n" +" move, vi-command és vi-insert.\n" +" -l Nevek és függvények listázása.\n" +" -P Függvénynevek és kötések listázása.\n" +" -p Függvények és kötések listázása újrahasználható\n" +" formában.\n" +" -S Makrókat végrehajtó billentyűkombinációk és értéke-\n" +" ik listázása.\n" +" -s Makrókat végrehajtó billentyűkombinációk és értéke-\n" +" ik listázása újrahasználható formában.\n" +" -V Változónevek és értékek listázása.\n" +" -v Változónevek és értékek listázása újrahasználható\n" +" formában.\n" +" -q függvénynév A függvényhez tartozó billentyűkombináció lekérése.\n" +" -u függvénynév Össze adott függvényhez tartozó billentyűkombiná-\n" +" ció törlése.\n" +" -r billkomb A BILLKOMB-hoz tartozó kötések törlése.\n" +" -f fájlnév Kötések olvasása FÃJLNÉV fájlból.\n" +" -x billkomb:shell-parancs SHELL-PARANCS végrehajtása BILLKOMB-ra.\n" +" \n" +" Kilépési kód:\n" +" a bind 0-val tér vissza, ha nincs ismeretlen kapcsoló vagy hiba." #: builtins.c:326 -#, fuzzy msgid "" "Exit for, while, or until loops.\n" " \n" @@ -2406,11 +2392,15 @@ msgid "" " Exit Status:\n" " The exit status is 0 unless N is not greater than or equal to 1." msgstr "" -"Folytatja a következõ egy szinttel magasabban elhelyezkedõ FOR, WHILE vagy " -"UNTIL hurokkal." +"Kilép a for, while vagy until ciklusokból.\n" +" \n" +" Kilép egy FOR, WHILE vagy UNTIL ciklusból. Ha N meg van adva, akkor N\n" +" egymásba ágyazott ciklusból lép ki.\n" +" \n" +" Kilépési kód:\n" +" A kilépési kód 0, ha N >= 1." #: builtins.c:338 -#, fuzzy msgid "" "Resume for, while, or until loops.\n" " \n" @@ -2420,8 +2410,14 @@ msgid "" " Exit Status:\n" " The exit status is 0 unless N is not greater than or equal to 1." msgstr "" -"Folytatja a következõ egy szinttel magasabban elhelyezkedõ FOR, WHILE vagy " -"UNTIL hurokkal." +"Újrakezdi a for, while vagy until ciklust.\n" +" \n" +" A következÅ‘ iterációtól folytatja a FOR, WHILE vagy UNTIL ciklust.\n" +" If N is specified, resumes the Nth enclosing loop.\n" +" Ha N meg van adva, akkor N egymásba ágyazott ciklusból lép ki.\n" +" \n" +" Kilépési kód:\n" +" A kilépési kód 0, ha N >= 1." #: builtins.c:350 msgid "" @@ -2429,13 +2425,21 @@ msgid "" " \n" " Execute SHELL-BUILTIN with arguments ARGs without performing command\n" " lookup. This is useful when you wish to reimplement a shell builtin\n" -" as a shell function, but need to execute the builtin within the " -"function.\n" +" as a shell function, but need to execute the builtin within the function.\n" " \n" " Exit Status:\n" " Returns the exit status of SHELL-BUILTIN, or false if SHELL-BUILTIN is\n" " not a shell builtin.." msgstr "" +"BeépÃtett parancsok végrehajtása.\n" +" \n" +" Végrehajtja SHELL-BUILTIN-t ARG argumentumokkal parancskeresés nélkül.\n" +" Ez akkor hasznos, ha felül szeretne definiálni egy beépÃtett parancsot\n" +" függvényként, de ebbÅ‘l meg szeretné hÃvni az eredeti parancsot.\n" +" \n" +" Kilépési kód:\n" +" Továbbadja a SHELL-BUILTIN kilépési kódját vagy hamissal tér vissza,\n" +" ha nincs ilyen parancs." #: builtins.c:365 msgid "" @@ -2452,27 +2456,33 @@ msgid "" " Returns 0 unless the shell is not executing a shell function or EXPR\n" " is invalid." msgstr "" +"Visszaadja az aktuális szubrutinhÃvás környezetét.\n" +" \n" +" KIF nélkül \"$sor $fájlnév\" formátumú eredményt ad. KIF-fel pedig\n" +" \"$sor $szubrutin $fájlnév\" formátumút; ez hasznos lehet stack trace\n" +" kiÃrásához.\n" +" \n" +" Az EXPR értéke azt adja meg, hogy a jelenlegihez képest milyen mélyre\n" +" lépjen vissza; a verem tetején a 0-s keret van.\n" +" \n" +" Kilépési kód:\n" +" 0-val tér vissza, ha érvényes KIF és valóban függvényt hajt végre a pa-\n" +" rancsértelmezÅ‘." #: builtins.c:383 msgid "" "Change the shell working directory.\n" " \n" -" Change the current directory to DIR. The default DIR is the value of " -"the\n" +" Change the current directory to DIR. The default DIR is the value of the\n" " HOME shell variable.\n" " \n" -" The variable CDPATH defines the search path for the directory " -"containing\n" -" DIR. Alternative directory names in CDPATH are separated by a colon " -"(:).\n" -" A null directory name is the same as the current directory. If DIR " -"begins\n" +" The variable CDPATH defines the search path for the directory containing\n" +" DIR. Alternative directory names in CDPATH are separated by a colon (:).\n" +" A null directory name is the same as the current directory. If DIR begins\n" " with a slash (/), then CDPATH is not used.\n" " \n" -" If the directory is not found, and the shell option `cdable_vars' is " -"set,\n" -" the word is assumed to be a variable name. If that variable has a " -"value,\n" +" If the directory is not found, and the shell option `cdable_vars' is set,\n" +" the word is assumed to be a variable name. If that variable has a value,\n" " its value is used for DIR.\n" " \n" " Options:\n" @@ -2485,6 +2495,29 @@ msgid "" " Exit Status:\n" " Returns 0 if the directory is changed; non-zero otherwise." msgstr "" +"A parancsértelmezÅ‘ munkakönyvtárát váltja.\n" +" \n" +" A munkakönyvtára KTÃR-ra váltja. Elhagyása esetén a HOME környezeti\n" +" változóban lévÅ‘ könyvtárra vált.\n" +" \n" +" A CDPATH környezeti változó adja meg a KTÃR keresési útvonalait. Az\n" +" útvonalakat kettÅ‘spont (:) válassza el. Egy üres könyvtárnév az aktu-\n" +" ális könyvtárat jelenti. Ha KTÃR „/†jellel kezdÅ‘dik, a CDPATH értéke\n" +" nincs figyelembe véve.\n" +" \n" +" Ha a könyvtár nem létezik, és a „cdable_vars†parancsértelmezÅ‘-beállÃ-\n" +" tás aktÃv, KTÃR egy változónévként lesz használva. Ha a változónak van\n" +" értéke, az lesz KTÃR-értékként használva.\n" +" \n" +" Kapcsolók:\n" +" -L szimbolikus linkek szigorú követése\n" +" -P a fizikai könyvtárfa használata a szimbolikus linkek követése\n" +" helyett\n" +" \n" +" Az alapértelmezett a szimbolikus linkek követése, mintha „-L†lenne\n" +" megadva.\n" +" Kilépési kód:\n" +" 0-val tér vissza, ha könyvtárat váltott; más értéket különben." #: builtins.c:411 msgid "" @@ -2501,9 +2534,21 @@ msgid "" " Returns 0 unless an invalid option is given or the current directory\n" " cannot be read." msgstr "" +"Az aktuális munkakönyvtár útvonalának kiÃrása.\n" +" \n" +" Kapcsolók:\n" +" -L $PWD értékének kiÃrása, ha az a munkakönyvtár érvényes\n" +" neve\n" +" -P a fizikai könyvtár kiÃrása, szimbolikus linkek nélkül\n" +" \n" +" Az alapértelmezett a szimbolikus linkek követése, mintha „-L†lenne\n" +" megadva.\n" +" \n" +" Kilépési kód:\n" +" 0-val tér vissza, kivéve ha érvénytelen kapcsolót kapott vagy nem le-\n" +" het olvasni a munkakönyvtárat." #: builtins.c:428 -#, fuzzy msgid "" "Null command.\n" " \n" @@ -2511,7 +2556,13 @@ msgid "" " \n" " Exit Status:\n" " Always succeeds." -msgstr "Nem csinál semmit ez a parancs. A visszatérési értéke 0." +msgstr "" +"Nincs művelet.\n" +" \n" +" Nincs hatása, a parancs nem csinál semmit.\n" +" \n" +" Kilépési kód:\n" +" Mindig sikeres." #: builtins.c:439 msgid "" @@ -2520,6 +2571,10 @@ msgid "" " Exit Status:\n" " Always succeeds." msgstr "" +"Sikeres visszatérés.\n" +" \n" +" Kilépési kód:\n" +" Mindig sikeres." #: builtins.c:448 msgid "" @@ -2528,14 +2583,17 @@ msgid "" " Exit Status:\n" " Always fails." msgstr "" +"Sikertelen visszatérés.\n" +" \n" +" Kilépési kód:\n" +" Mindig sikertelen." #: builtins.c:457 msgid "" "Execute a simple command or display information about commands.\n" " \n" " Runs COMMAND with ARGS suppressing shell function lookup, or display\n" -" information about the specified COMMANDs. Can be used to invoke " -"commands\n" +" information about the specified COMMANDs. Can be used to invoke commands\n" " on disk when a function with the same name exists.\n" " \n" " Options:\n" @@ -2547,6 +2605,22 @@ msgid "" " Exit Status:\n" " Returns exit status of COMMAND, or failure if COMMAND is not found." msgstr "" +"Egy parancsot hajt végre vagy információt jelenÃt meg róla.\n" +" \n" +" Végrehajtja a PARANCS parancsot ARGUMENTUMOK argumentumokkal a függ-\n" +" vényfeloldás végrehajtása nélkül; vagy információt jelenÃt meg a pa-\n" +" rancsról. Használható programok futtatására, ha azonos nevű függvény\n" +" létezik.\n" +" \n" +" Kapcsolók:\n" +" -p alapértelmezett érték használata PATH helyett, amely ga-\n" +" rantáltan megtalál minden szabványos eszközt -v egy leÃrást ad a PARANCS parancsról a „type†beépÃtett pa-\n" +" rancshoz hasonló módon\n" +" -V minden PARANCS-ról egy részletesebb leÃrást ad\n" +" \n" +" Kilépési kód:\n" +" PARANCS kilépési kódjával tér vissza, vagy hibát jelez, ha nem talál-\n" +" ható PARANCS." #: builtins.c:476 msgid "" @@ -2576,13 +2650,45 @@ msgid "" " Variables with the integer attribute have arithmetic evaluation (see\n" " the `let' command) performed when the variable is assigned a value.\n" " \n" -" When used in a function, `declare' makes NAMEs local, as with the " -"`local'\n" +" When used in a function, `declare' makes NAMEs local, as with the `local'\n" " command.\n" " \n" " Exit Status:\n" " Returns success unless an invalid option is supplied or an error occurs." msgstr "" +"Változóértékeket és jellemzÅ‘ket állÃt be.\n" +" \n" +" Változók deklarálása és jellemzÅ‘k adása. Ha nincs NÉV megadva, kilis-\n" +" tázza az összes változó jellemzÅ‘it és értékét.\n" +" \n" +" Kapcsolók:\n" +" -f művelet és megjelenÃtés korlátozása függvénynevekre és\n" +" -definÃciókra\n" +" -F megjelenÃtés korlátozása függvénynevekre (és sor számára,\n" +" valamint a forrásfájl nevére hibakereséskor)\n" +" -p minden NÉV jellemzÅ‘inek és értékének kiÃrása\n" +" \n" +" JellemzÅ‘ket állÃtó kapcsolók:\n" +" -a NÉV indexelt tömbbé alakÃtása (ha támogatott)\n" +" -A NÉV asszociatÃv tömbbé alakÃtása (ha támogatott)\n" +" -i minden NÉV kapjon „integer†jellemzÅ‘t\n" +" -l NÉV-hez való értékadáskor kisbetűssé konvertálás\n" +" -r minden NÉV legyen csak olvasható\n" +" -t minden NÉV kapjon „trace†jellemzÅ‘t\n" +" -u NÉV-hez való érékadáskor nagybetűssé konvertálás\n" +" -x minden NÉV exportálása\n" +" \n" +" A „-†helyett „+†használata kikapcsolja az adott jellemzÅ‘t.\n" +" \n" +" Az integer jellemzÅ‘vel rendelkezÅ‘ változókhoz való értékadáskor arit-\n" +" metikai kiértékelés történik (lásd a „let†parancsot).\n" +" \n" +" Függvénytörzsben „declareâ€-t használva minden NÉV helyi lesz, hasonló-\n" +" an a „local†parancshoz.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha érvénytelen kapcsolót kap, vagy hiba\n" +" történt." #: builtins.c:512 msgid "" @@ -2590,6 +2696,9 @@ msgid "" " \n" " Obsolete. See `help declare'." msgstr "" +"Változóértékeket és jellemzÅ‘ket állÃt be.\n" +" \n" +" Elavult. Lásd „help declareâ€." #: builtins.c:520 msgid "" @@ -2605,6 +2714,17 @@ msgid "" " Returns success unless an invalid option is supplied, an error occurs,\n" " or the shell is not executing a function." msgstr "" +"Helyi változók definiálása.\n" +" \n" +" Egy NÉV nevű helyi változót hoz létre, és ÉRTÉK értéket ad neki. KAP-\n" +" CSOLÓ tetszÅ‘leges „declare†által elfogadott kapcsoló lehet.\n" +" \n" +" A helyi változók csak a függvényen belül használhatóak, nem láthatóak\n" +" az Å‘ket definiáló függvényen és azok gyermekein kÃvül.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha érvénytelen kapcsolót kap, hiba\n" +" történt, vagy nem függvényben lett hÃvva." #: builtins.c:537 msgid "" @@ -2636,6 +2756,33 @@ msgid "" " Exit Status:\n" " Returns success unless a write error occurs." msgstr "" +"Argumentumok szabványos kimenetre Ãrása.\n" +" \n" +" Argumentumok és egy újsor kiÃrása a szabványos kimenetre.\n" +" \n" +" Kapcsolók:\n" +" -n ne fűzzön hozzá újsort\n" +" -e az alábbi escape-szekvenciák értelmezése\n" +" -E escape-szekvenciák értelmezésének tiltása\n" +" \n" +" Az „echoâ€a következÅ‘ visszaper-escape-karaktereket értelmezi:\n" +" \\a terminálcsengÅ‘\n" +" \\b visszatörlés (backspace)\n" +" \\c további kimenet elnyelése\n" +" \\e escape-karakter\n" +" \\f lapdobás-karakter\n" +" \\n újsor-karakter\n" +" \\r kocsivissza-karakter\n" +" \\t vÃzszintes tabulátor\n" +" \\v függÅ‘leges tabulátor\n" +" \\\\ visszaper (\\)\n" +" \\0nnn az oktális NNN ASCII-kódú karakter. NNN 0–3\n" +" oktális számjegy lehet\n" +" \\xHH az a 8 bites karakter, amelynek értéke HH\n" +" (hexadecimálisan). HH egy vagy két hexaszámjegy lehet\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve Ãrási hiba esetén." #: builtins.c:571 msgid "" @@ -2649,6 +2796,15 @@ msgid "" " Exit Status:\n" " Returns success unless a write error occurs." msgstr "" +"Argumentumok szabványos kimenetre Ãrása.\n" +" \n" +" Argumentumok és egy újsor kiÃrása a szabványos kimenetre.\n" +" \n" +" Kapcsolók:\n" +" -n ne fűzzön hozzá újsort\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve Ãrási hiba esetén." #: builtins.c:586 msgid "" @@ -2676,18 +2832,50 @@ msgid "" " Exit Status:\n" " Returns success unless NAME is not a shell builtin or an error occurs." msgstr "" +"ParancsértelmezÅ‘ beépÃtett parancsainak engedélyezése és tiltása.\n" +" \n" +" BeépÃtett parancsokat engedélyez és tilt. Egy parancs letiltásával az\n" +" elérési út beÃrása nélkül lehet beépÃtett paranccsal megegyezÅ‘ nevű-\n" +" programot futtatni.\n" +" \n" +" Kapcsolók:\n" +" -a a beépÃtett parancsok és azok állapotának listázása\n" +" -n minden NÉV tiltása vagy a tiltott parancsok listázása\n" +" -p a beépÃtett parancsokat listázza újrahasználható formában\n" +" -s csak a Posix „special†beépÃtett parancsokat listázza\n" +" \n" +" Dinamikus betöltést szabályozó kapcsolók:\n" +" -f NÉV nevű beépÃtett parancs betöltése a FÃJLNÉV megosztott objek-\n" +" tumfájlból\n" +" -d egy -f kapcsolóval betöltött parancs eltávolÃtása\n" +" \n" +" Kapcsolók nélkül minden NÉV engedélyezésre kerül\n" +" \n" +" A beépÃtett parancs helyett a $PATH-ban található „test†használatához\n" +" használja az „enable -n test†parancsot.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha NÉV nem egy beépÃtett parancs, vagy hi-\n" +" ba történt." #: builtins.c:614 msgid "" "Execute arguments as a shell command.\n" " \n" -" Combine ARGs into a single string, use the result as input to the " -"shell,\n" +" Combine ARGs into a single string, use the result as input to the shell,\n" " and execute the resulting commands.\n" " \n" " Exit Status:\n" " Returns exit status of command or success if command is null." msgstr "" +"Argumentumok végrehajtása parancsként.\n" +" \n" +" Az argumentumokat összefűzi, és az eredményt egy parancssorként hajtja\n" +" végre a parancsértelmezÅ‘.\n" +" \n" +" Kilépési kód:\n" +" A parancs kilépési kódjával tér vissza, vagy sikerrel, ha üres a pa-\n" +" rancs." #: builtins.c:626 msgid "" @@ -2729,14 +2917,46 @@ msgid "" " Returns success if an option is found; fails if the end of options is\n" " encountered or an error occurs." msgstr "" +"Kapcsolók értelmezése.\n" +" \n" +" A getopts parancsot arra használják az eljárások, hogy pozicionális\n" +" paramétereket kapcsolókként értelmezzenek.\n" +" \n" +" A KAPCSOLÓK azokat a betűket tartalmazza, amelyeket fel kell ismerni.\n" +" Ha egy kapcsolót kettÅ‘spont követ, a kapcsoló kötelezÅ‘ paramétert vár.\n" +" Ezt a paramétert szóközzel kell elválasztani a kapcsolótól.\n" +" \n" +" Minden végrehajtáskor a getopts a $név változóba helyezi a következÅ‘\n" +" kapcsolót (szükség esetén inicializálva a változót). A kapcsoló indexe\n" +" az OPTIND változóba kerül. Az OPTIND változót a parancsértelmezÅ‘ indu-\n" +" láskor 1-re inicializálja. Ha a kapcsolónak paramétere van, ennek ér-\n" +" téke az OPTARG változóba kerül.\n" +" \n" +" A getopts két módon tud hibát jelezni. ElnémÃtható a hibajelzés az OP-\n" +" CIÓK kettÅ‘sponttal való kezdésével. Ebben a módban nem kerül kiÃrásra\n" +" hibaüzenet. Ha a getopts érvénytelen opciót talál, ezt az OPTARG vál-\n" +" tozóba Ãrja. Ha hiányzik egy kötelezÅ‘ paraméter, a $név változóba egy\n" +" kettÅ‘spont kerül, és a talált karakter OPTARG-ba kerül.\n" +" Ha a getopts nincs néma módban, és érvénytelen kapcsolót talál, $név-\n" +" be egy kérdÅ‘jel kerül, OPTARG törlésre kerül, és hibaüzenetet Ãr ki.\n" +" \n" +" Ha az OPTERR változó 0-ra van állÃtva, a getopts letiltja a hibaüzene-\n" +" tet, akkor is, ha nem kettÅ‘sponttal kezdÅ‘dik az OPCIÓK. OPTERR alapér-\n" +" téke 1.\n" +" \n" +" A getopts alapvetÅ‘en pozicionális paramétereket értelmezi ($0–$9), de\n" +" több argumentum esetén mindet kezeli.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, ha kapcsolót talált, sikertelenül, ha elfogytak a\n" +" kapcsolók vagy hiba történt." #: builtins.c:668 msgid "" "Replace the shell with the given command.\n" " \n" " Execute COMMAND, replacing this shell with the specified program.\n" -" ARGUMENTS become the arguments to COMMAND. If COMMAND is not " -"specified,\n" +" ARGUMENTS become the arguments to COMMAND. If COMMAND is not specified,\n" " any redirections take effect in the current shell.\n" " \n" " Options:\n" @@ -2744,46 +2964,65 @@ msgid "" " -c\t\texecute COMMAND with an empty environment\n" " -l\t\tplace a dash in the zeroth argument to COMMAND\n" " \n" -" If the command cannot be executed, a non-interactive shell exits, " -"unless\n" +" If the command cannot be executed, a non-interactive shell exits, unless\n" " the shell option `execfail' is set.\n" " \n" " Exit Status:\n" -" Returns success unless COMMAND is not found or a redirection error " -"occurs." +" Returns success unless COMMAND is not found or a redirection error occurs." msgstr "" +"A parancsértelmezÅ‘ felváltása a megadott paranccsal.\n" +" \n" +" PARANCS végrehajtása, kicserélve a parancsértelmezÅ‘t a megadott prog-\n" +" rammal. ARGUMENTUMOK lesznek a PARANCS argumentumai. Ha nincs PARANCS\n" +" megadva, a futó shellre kerülnek érvényesÃtésre az átirányÃtások.\n" +" \n" +" Kapcsolók:\n" +" -a név NÉV átadása PARANCS-nak $0-ként\n" +" -c PARANCS végrehajtása üres környezettel\n" +" -l PARANCS-nak egy „-†átadása $0-ként\n" +" \n" +" Ha a parancs nem hajtható végre, a nem interaktÃv parancsértelmezÅ‘ ki-\n" +" lép, kivéve, ha az „execfail†parancsértelmezÅ‘-beállÃtás él.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve, ha PARANCS nem található vagy sikertelen\n" +" az átirányÃtás." #: builtins.c:689 -#, fuzzy msgid "" "Exit the shell.\n" " \n" " Exits the shell with a status of N. If N is omitted, the exit status\n" " is that of the last command executed." -msgstr "Kilép a parancsértelmezõbõl N státusszal. Ha az N-t kihagyja akkor a " +msgstr "" +"Kilép a parancsértelmezÅ‘bÅ‘l.\n" +" \n" +" Kilép a parancsértelmezÅ‘bÅ‘l N kilépési kóddal. Ha N hiányzik, az utol-\n" +" só parancs kilépési kódjával lép ki." #: builtins.c:698 msgid "" "Exit a login shell.\n" " \n" -" Exits a login shell with exit status N. Returns an error if not " -"executed\n" +" Exits a login shell with exit status N. Returns an error if not executed\n" " in a login shell." msgstr "" +"Kilép a bejelentkezÅ‘ parancsértelmezÅ‘bÅ‘l.\n" +" \n" +" Kilép a bejelentkezÅ‘ parancsértelmezÅ‘bÅ‘l N kilépési kóddal. Hibával\n" +" tér vissza, ha nem bejelentkezÅ‘ parancsértelmezÅ‘bÅ‘l hÃvják." #: builtins.c:708 msgid "" "Display or execute commands from the history list.\n" " \n" -" fc is used to list or edit and re-execute commands from the history " -"list.\n" +" fc is used to list or edit and re-execute commands from the history list.\n" " FIRST and LAST can be numbers specifying the range, or FIRST can be a\n" " string, which means the most recent command beginning with that\n" " string.\n" " \n" " Options:\n" -" -e ENAME\tselect which editor to use. Default is FCEDIT, then " -"EDITOR,\n" +" -e ENAME\tselect which editor to use. Default is FCEDIT, then EDITOR,\n" " \t\tthen vi\n" " -l \tlist lines instead of editing\n" " -n\tomit line numbers when listing\n" @@ -2797,12 +3036,34 @@ msgid "" " the last command.\n" " \n" " Exit Status:\n" -" Returns success or status of executed command; non-zero if an error " -"occurs." +" Returns success or status of executed command; non-zero if an error occurs." msgstr "" +"Parancsok megjelenÃtése vagy végrehajtása az elÅ‘zménybÅ‘l.\n" +" \n" +" Az fc segÃtségével lehet korábbi parancsokat kiÃrni, módosÃtani és új-\n" +" ból végrehajtani.\n" +" ELSŠés UTOLSÓ lehetnek egy tartományt meghatározó számok, vagy ELSÅ\n" +" lehet egy karakterlánc, amely az utolsó Ãgy kezdÅ‘dÅ‘ parancsot jelöli.\n" +" \n" +" Kapcsolók:\n" +" -e ENAME szerkesztÅ‘ kiválasztása. Az alapértelmezett az FCEDIT,\n" +" majd EDITOR, végül a vi\n" +" -l szerkesztés helyett a sorok listázása\n" +" -n sorok számának elhagyása listázáskor\n" +" -r sorrend megcserélése (legújabbakkal kezdi a listázást)\n" +" \n" +" Az „fc -s [minta=csere] [parancs]†formával PARANCS... újból végrehaj-\n" +" tásra kerül miután a régi=új behelyettesÃtés megtörtént.\n" +" \n" +" Hasznos lehet az „alias r='fc -s'†használata, mivel Ãgy pl. az „r ccâ€\n" +" parancs végrehajtja az utolsó „ccâ€-vel kezdÅ‘dÅ‘ parancsot, mÃg „r†meg-\n" +" ismétli az utolsó parancsot.\n" +" \n" +" Kilépési kód:\n" +" Sikert vagy a végrehajtott parancs kilépési kódját adja; nullától el-\n" +" térÅ‘t hiba esetén." #: builtins.c:738 -#, fuzzy msgid "" "Move job to the foreground.\n" " \n" @@ -2813,30 +3074,43 @@ msgid "" " Exit Status:\n" " Status of command placed in foreground, or failure if an error occurs." msgstr "" -"A munka_folyamat-ot az elõtérbe helyezi és jelenlegi folyamatot csinál " -"belõle." +"A munka elÅ‘térbe hozása.\n" +" \n" +" A MUNKASZÃM által meghatározott munkát az elÅ‘térbe hozza, az aktuális\n" +" munkává téve azt. Ha nincs MUNKASZÃM, a parancsértelmezÅ‘ által meg-\n" +" jegyzett aktuális munkára vonatkozik a parancs.\n" +" \n" +" Kilépési kód:\n" +" Az elÅ‘térbe hozott parancs állapota (annak kilépésekor), vagy nemnulla\n" +" hiba esetén." #: builtins.c:753 msgid "" "Move jobs to the background.\n" " \n" -" Place the jobs identified by each JOB_SPEC in the background, as if " -"they\n" -" had been started with `&'. If JOB_SPEC is not present, the shell's " -"notion\n" +" Place the jobs identified by each JOB_SPEC in the background, as if they\n" +" had been started with `&'. If JOB_SPEC is not present, the shell's notion\n" " of the current job is used.\n" " \n" " Exit Status:\n" " Returns success unless job control is not enabled or an error occurs." msgstr "" +"Munkák háttérbe küldése.\n" +" \n" +" A MUNKASZÃM által meghatározott munkákat háttérbe küldi, mintha „&â€\n" +" jellel a parancs végén lettek volna indÃtva. Ha nincs MUNKASZÃM, a pa-\n" +" rancsértelmezÅ‘ által megjegyzett aktuális munkára vonatkozik a parancs.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha a munkakezelés le van tiltva, vagy hi-\n" +" ba történt." #: builtins.c:767 msgid "" "Remember or display program locations.\n" " \n" " Determine and remember the full pathname of each command NAME. If\n" -" no arguments are given, information about remembered commands is " -"displayed.\n" +" no arguments are given, information about remembered commands is displayed.\n" " \n" " Options:\n" " -d\t\tforget the remembered location of each NAME\n" @@ -2853,6 +3127,25 @@ msgid "" " Exit Status:\n" " Returns success unless NAME is not found or an invalid option is given." msgstr "" +"Programok helyének megjegyzése vagy megjelenÃtése.\n" +" \n" +" Meghatározza vagy megjegyzi a teljes útvonalát minden megadott NÉV\n" +" parancsnak. Ha nincs NÉV megadva, az összes megjegyzett parancsot lis-\n" +" tázza.\n" +" \n" +" Kapcsolók:\n" +" -d minden megjegyzett NÉV helyének elfelejtése\n" +" -l bemenetként újrahasználható formátumban listázzon\n" +" -p útvonal ÚTVONAL használata NÉV helyeként\n" +" -r minden megjegyzett hely elfelejtése\n" +" -t minden megadott NÉV megjegyzett helyének kiÃrása,\n" +" több név esetén a helyek elÅ‘tt a NÉV kiÃrása\n" +" Argumentumok:\n" +" NÉV Minden NEV-et megkeres a $PATH-ban, és hozzáadja a megjegy-\n" +" zettek listájához. \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve, ha NÉV nem található vagy érvénytelen kap-\n" +" csolót kap." #: builtins.c:792 msgid "" @@ -2872,9 +3165,24 @@ msgid "" " PATTERN\tPattern specifiying a help topic\n" " \n" " Exit Status:\n" -" Returns success unless PATTERN is not found or an invalid option is " -"given." +" Returns success unless PATTERN is not found or an invalid option is given." msgstr "" +"Tájékoztatás megjelenÃtése beépÃtett parancsokról.\n" +" \n" +" Rövid leÃrásokat jelenÃt meg a beépÃtett parancsokról. Ha MINTA meg\n" +" van adva, részletes segÃtséget ad az összes illeszkedÅ‘ parancsról, kü-\n" +" lönben a témákat listázza.\n" +" \n" +" Kapcsolók:\n" +" -d minden témáról rövid leÃrás listázása\n" +" -m man-szerű formátum használata\n" +" -s csak rövid használati útmutató kiÃrása minden találathoz\n" +" \n" +" Argumentumok:\n" +" MINTA Témakört meghatározó minta\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha nincs találat vagy hibás kapcsolót kap." #: builtins.c:816 msgid "" @@ -2903,12 +3211,40 @@ msgid "" " \n" " If the $HISTTIMEFORMAT variable is set and not null, its value is used\n" " as a format string for strftime(3) to print the time stamp associated\n" -" with each displayed history entry. No time stamps are printed " -"otherwise.\n" +" with each displayed history entry. No time stamps are printed otherwise.\n" " \n" " Exit Status:\n" " Returns success unless an invalid option is given or an error occurs." msgstr "" +"MegjelenÃti vagy módosÃtja az elÅ‘zményeket.\n" +" \n" +" MegjelenÃti az elÅ‘zménylistát sorszámokkal, minden módosÃtott bejegy-\n" +" zést az elején „*â€-gal megjelölve. N megadása esetén az utolsó N be-\n" +" jegyzést listázza.\n" +" \n" +" Kapcsolók:\n" +" -c minden elÅ‘zmény törlése\n" +" -d szám a SZÃM számú bejegyzés törlése\n" +" -a a futó munkamenet elÅ‘zményeinek központi fájlba Ãrása\n" +" -n minden olvasatlan elÅ‘zménysor kiÃrása az elÅ‘zményfájlból\n" +" -r elÅ‘zményfájl beolvasása és elÅ‘zménylistához Ãrása\n" +" -w az aktuális elÅ‘zmények elÅ‘zményfájlba Ãrása és elÅ‘zmény-\n" +" listához Ãrása\n" +" \n" +" -p elÅ‘zménykiegészÃtés végrehajtása minden ARGumentumon és az\n" +" eredmény kiÃrása elÅ‘zménylistán való tárolás nélkül\n" +" -s ARGumentumok hozzáÃrása egyetlen bejegyzésként a listához\n" +" \n" +" Ha FÃJLNÉV is meg van adva, az lesz elÅ‘zményfájlként használva. Külön-\n" +" ben $HISTFILE értéke, vagy ennek hÃján ~/.bash_history.\n" +" \n" +" Ha a $HISTTIMEFORMAT változó be van állÃtva, és nem üres, akkor értéke\n" +" lesz használva az strftime(3) formátumparamétereként a kijelzett be-\n" +" jegyzések idÅ‘bélyegeinek megjelenÃtéséhez. Különben nem Ãr ki idÅ‘t.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha érvénytelen kapcsolót kap, vagy hiba\n" +" történik." #: builtins.c:852 msgid "" @@ -2933,6 +3269,26 @@ msgid "" " Returns success unless an invalid option is given or an error occurs.\n" " If -x is used, returns the exit status of COMMAND." msgstr "" +"Munkák állapotának megjelenÃtése.\n" +" \n" +" Listáz minden aktÃv munkát. MUNKASZÃM megadása esetén csak az adott\n" +" munka jelenik meg, különben az összes aktÃv.\n" +" \n" +" Kapcsolók:\n" +" -l folyamatazonosÃtók megjelenÃtése a többi adaton túl\n" +" -n csak azon folyamatok listázása, amelyek állapota változott\n" +" az utolsó értesÃtés óta\n" +" -p csak folyamatazonosÃtók listázása\n" +" -r csak a futó munkák megjelenÃtése\n" +" -s csak a megállÃtott munkák megjelenÃtése\n" +" \n" +" Ha -x meg van adva, PARANCS kerül futtatásra úgy, hogy minden argumen-\n" +" tum a meghatározott munkához tartozó folyamatcsoport vezetÅ‘jének PID-\n" +" jére cserélÅ‘dik.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, ha nem kap érvénytelen kapcsolót és nem történik\n" +" hiba. -x használata esetén PARANCS kilépési kódjával tér vissza." #: builtins.c:879 msgid "" @@ -2950,6 +3306,21 @@ msgid "" " Exit Status:\n" " Returns success unless an invalid option or JOBSPEC is given." msgstr "" +"Munkák eltávolÃtása az aktuális parancsértelmezÅ‘bÅ‘l.\n" +" \n" +" EltávolÃt minden MUNKASZÃM munkát az aktÃv munkák táblájából. MUNKA-\n" +" SZÃM megadása nélkül a parancsértelmezÅ‘ által megjegyzett aktuális\n" +" munkát távolÃtja el.\n" +" \n" +" Kapcsolók:\n" +" -a minden munka eltávolÃtása, ha nincs MUNKASZÃM megadva\n" +" -h minden MUNKASZÃM megjelölése úgy, hogy nem kell továbbadni\n" +" nekik a parancsértelmezÅ‘ által kapott SIGHUP-ot\n" +" -r csak futó munkák eltávolÃtása\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, ha nem kap érvénytelen kapcsolót vagy MUNKASZÃM-\n" +" ot." #: builtins.c:898 msgid "" @@ -2972,6 +3343,25 @@ msgid "" " Exit Status:\n" " Returns success unless an invalid option is given or an error occurs." msgstr "" +"Szignál küldése munkának.\n" +" \n" +" PID vagy MUNKASZÃM által meghatározott folyamatoknak SZIGNÃL vagy\n" +" SZIGNÃLSZÃM szignál küldése. Ha sem SZIGNÃL, sem SZIGNÃLSZÃM nincs\n" +" megadva, akkor SIGTERM az alapértelmezés.\n" +" \n" +" Kapcsolók:\n" +" -s sig SIG egy szignálnév\n" +" -n sig SIG egy szignálszám\n" +" -l a szignálnevek listázása; ha argumentumok is követik, akkor\n" +" az általuk meghatározott szignálok nevei kerülnek listázásra\n" +" \n" +" A kill két okból beépÃtett parancs: Ãgy lehetÅ‘vé teszi munkaszámok\n" +" használatát PID helyett, továbbá lehetségessé válik folyamatok kilövé-\n" +" se, ha a folyamatok számának korlátja kimerült.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, ha nem kap érvénytelen kapcsolót és nem történik\n" +" hiba." #: builtins.c:921 msgid "" @@ -2980,8 +3370,7 @@ msgid "" " Evaluate each ARG as an arithmetic expression. Evaluation is done in\n" " fixed-width integers with no check for overflow, though division by 0\n" " is trapped and flagged as an error. The following list of operators is\n" -" grouped into levels of equal-precedence operators. The levels are " -"listed\n" +" grouped into levels of equal-precedence operators. The levels are listed\n" " in order of decreasing precedence.\n" " \n" " \tid++, id--\tvariable post-increment, post-decrement\n" @@ -3017,22 +3406,57 @@ msgid "" " Exit Status:\n" " If the last ARG evaluates to 0, let returns 1; let returns 0 otherwise.." msgstr "" +"Aritmetikai kifejezés kiértékelése.\n" +" \n" +" Minden ARGumentum kiértékelése aritmetikai kifejezésként. A kiértéke-\n" +" lés fix szélességű egészek esetén túlcsordulás-ellenÅ‘rzés nélkül törté-\n" +" nik, de a nullával való osztás hibát okoz. Az alábbi operátorok soron-\n" +" ként azonos precedenciaszinten vannak. A precedencia az alábbi sorrend-\n" +" ben csökken:\n" +" \n" +" id++, id-- változó postfix-növelése, -csökkentése\n" +" ++id, --id változó prefix-növelése, -csökkentése\n" +" -, + mÃnusz, plusz elÅ‘jel\n" +" !, ~ logikai és bitenkénti negált\n" +" ** hatványozás\n" +" *, /, % szorzás, osztás, maradék\n" +" +, - összeadás, kivonás\n" +" <<, >> bitenkénti eltolás balra, jobb\n" +" <=, >=, <, > összehasonlÃtás\n" +" ==, != egyenlÅ‘ség, egyenlÅ‘tlenség\n" +" & bitenkénti ÉS\n" +" ^ bitenkénti kizáró vagy\n" +" | bitenkénti VAGY\n" +" && logikai ÉS\n" +" || logikai VAGY\n" +" kif ? kif : kif\n" +" feltételes operátor\n" +" =, *=, /=, %=,\n" +" +=, -=, <<=, >>=,\n" +" &=, ^=, |= értékadás\n" +" \n" +" ParancsértelmezÅ‘-változók is lehetnek operandusok. A változók nevének\n" +" helyére értékük kerül (fix szélességű egészként) a kifejezésben. Nem\n" +" kell a változók „integer†jellemzÅ‘jét beállÃtani a használathoz.\n" +" \n" +" Az operátorok a fenti precedencia szerint hajtódnak végre. A zárójeles\n" +" kifejezések precedenciája a legmagasabb – ez felülÃrja a szabályokat.\n" +" \n" +" Kilépési kód:\n" +" Ha az utolsó argumentum 0, a let 1-gyel tér vissza, különben 0-val." #: builtins.c:966 msgid "" "Read a line from the standard input and split it into fields.\n" " \n" " Reads a single line from the standard input, or from file descriptor FD\n" -" if the -u option is supplied. The line is split into fields as with " -"word\n" +" if the -u option is supplied. The line is split into fields as with word\n" " splitting, and the first word is assigned to the first NAME, the second\n" " word to the second NAME, and so on, with any leftover words assigned to\n" -" the last NAME. Only the characters found in $IFS are recognized as " -"word\n" +" the last NAME. Only the characters found in $IFS are recognized as word\n" " delimiters.\n" " \n" -" If no NAMEs are supplied, the line read is stored in the REPLY " -"variable.\n" +" If no NAMEs are supplied, the line read is stored in the REPLY variable.\n" " \n" " Options:\n" " -a array\tassign the words read to sequential indices of the array\n" @@ -3044,15 +3468,13 @@ msgid "" " -n nchars\treturn after reading NCHARS characters rather than waiting\n" " \t\tfor a newline, but honor a delimiter if fewer than NCHARS\n" " \t\tcharacters are read before the delimiter\n" -" -N nchars\treturn only after reading exactly NCHARS characters, " -"unless\n" +" -N nchars\treturn only after reading exactly NCHARS characters, unless\n" " \t\tEOF is encountered or read times out, ignoring any delimiter\n" " -p prompt\toutput the string PROMPT without a trailing newline before\n" " \t\tattempting to read\n" " -r\t\tdo not allow backslashes to escape any characters\n" " -s\t\tdo not echo input coming from a terminal\n" -" -t timeout\ttime out and return failure if a complete line of input " -"is\n" +" -t timeout\ttime out and return failure if a complete line of input is\n" " \t\tnot read withint TIMEOUT seconds. The value of the TMOUT\n" " \t\tvariable is the default timeout. TIMEOUT may be a\n" " \t\tfractional number. If TIMEOUT is 0, read returns success only\n" @@ -3061,10 +3483,44 @@ msgid "" " -u fd\t\tread from file descriptor FD instead of the standard input\n" " \n" " Exit Status:\n" -" The return code is zero, unless end-of-file is encountered, read times " -"out,\n" +" The return code is zero, unless end-of-file is encountered, read times out,\n" " or an invalid file descriptor is supplied as the argument to -u." msgstr "" +"Beolvas egy sort a szabványos bemenetrÅ‘l és mezÅ‘kre osztja.\n" +" \n" +" Egy sort olvas be a szabványos bemenetrÅ‘l, vagy az FD fájlleÃróból, ha\n" +" meg van adva a -u kapcsoló. A sor mezÅ‘kre lesz osztva a szódarabolás\n" +" szabályai szerint. Az elsÅ‘ szó az elÅ‘s NÉV nevű változó értéke lesz, a\n" +" második a másodiké stb. A szóelválasztó karaktereket az $IFS adja.\n" +" \n" +" Ha nincs NÉV megadva, a beolvasott sor a $REPLY változóba kerül.\n" +" \n" +" Kapcsolók:\n" +" -a tömb a beolvasott szavak TÖMB tömb 0-tól kezdve egymást követÅ‘\n" +" indexű elemeibe kerülnek\n" +" -d elvál ELVÃL elsÅ‘ karakteréig olvasson az újsor helyett\n" +" -e a sor beolvasása Readline használatával interaktÃvan\n" +" -i szöveg SZÖVEG használata kezdeti szövegként (Readlinehoz)\n" +" -n szám SZÃM karakter beolvasása után térjen vissza, ne várjon egy\n" +" újsorra, de vegye figyelembe az elválasztót, ha kevesebb\n" +" mint SZÃM karaktert olvasott be az elválasztóig\n" +" -N szám pontosan akkor térjen vissza, ha SZÃM karaktert olva-\n" +" sott be, kivéve az EOF elérését és az idÅ‘túllépést, az el-\n" +" választó figyelmen kÃvül hagyva\n" +" -p prompt Ãrja ki a PROMPT értékét olvasás elÅ‘tt a sor elejére\n" +" -r tiltsa le a „\\†kezdetű escape-ek használatát\n" +" -s terminálról érkezÅ‘ bemenet ne visszhangozzon\n" +" -t idÅ‘ IDÅ leteltével jelezzen hibát, ha nem tudott egy teljes\n" +" sort beolvasni. A $TMOUT változó értéke az alapértelmezett\n" +" idÅ‘korlát. IDÅ lehet tizedestört is. Ha idÅ‘ 0, csak akkor\n" +" lesz sikeres a beolvasás, ha az adott fájlleÃrón már ol-\n" +" vasható a bemenet. IdÅ‘túllépés esetén a kilépési kód >128\n" +" -u fd fájl beolvasása FD. fájlleÃróból a szabványos bemenet he-\n" +" lyett\n" +" \n" +" Kilépési kód:\n" +" A kilépési kód nulla, kivéve ha EOF-ot ér a beolvasás, idÅ‘túllépéskor\n" +" vagy érvénytelen fájlleÃró megadásakor." #: builtins.c:1009 msgid "" @@ -3077,6 +3533,15 @@ msgid "" " Exit Status:\n" " Returns N, or failure if the shell is not executing a function or script." msgstr "" +"Visszatér egy függvénybÅ‘l.\n" +" \n" +" Egy függvény vagy egy „sourceâ€-olt parancsfájl adott N kilépési kóddal\n" +" való visszatérését okozza. Ha N nincs megadva, az utolsó parancs kilé-\n" +" pési kódjával tér vissza.\n" +" \n" +" Kilépési kód:\n" +" N-nel tér vissza, kivéve ha nem függvénybÅ‘l vagy parancsfájlból akar\n" +" visszatérni – ekkor sikertelenséget jelez." #: builtins.c:1022 msgid "" @@ -3121,8 +3586,7 @@ msgid "" " physical same as -P\n" " pipefail the return value of a pipeline is the status of\n" " the last command to exit with a non-zero status,\n" -" or zero if no command exited with a non-zero " -"status\n" +" or zero if no command exited with a non-zero status\n" " posix change the behavior of bash where the default\n" " operation differs from the Posix standard to\n" " match the standard\n" @@ -3159,6 +3623,83 @@ msgid "" " Exit Status:\n" " Returns success unless an invalid option is given." msgstr "" +"ParancsértelmezÅ‘-beállÃtások és pozicionális paraméterek állÃtása, törlése.\n" +" \n" +" ParancsértelmezÅ‘-jellemzÅ‘k és pozicionális paraméterek értékeinek mó-\n" +" dosÃtása, parancsértelmezÅ‘-változók neveinek és értékeinek kiÃrása.\n" +" display the names and values of shell variables.\n" +" \n" +" Kapcsolók:\n" +" -a A módosÃtott vagy létrehozott változó exportálásra jelölése\n" +" -b Munka befejezésérÅ‘l azonnali értesÃtés\n" +" -e Azonnali kilépés, ha egy parancs nem nullával lép ki\n" +" -f Fájlnév-generálás (globbing) tiltása\n" +" -h Parancsok helyének megjegyzése használatkor\n" +" -k Minden értékadó argumentum a parancs környezetébe kerül, nem\n" +" csak a parancsot megelÅ‘zÅ‘k\n" +" -m Munkakezelés engedélyezése\n" +" -n Parancsok beolvasása végrehajtás nélkül\n" +" -o kapcsolónév\n" +" Kapcsolónév szerinti változóállÃtás:\n" +" allexport mint -a\n" +" braceexpand mint -B\n" +" emacs emacs-szerű sorszerkesztés\n" +" errexit mint -e\n" +" errtrace mint -E\n" +" functrace mint -T\n" +" hashall mint -h\n" +" histexpand mint -H\n" +" history elÅ‘zmények tárolásának engedélyezése\n" +" ignoreeof EOF esetén nem lép ki a parancsértelmezÅ‘\n" +" interactive-comments\n" +" interaktÃv parancsokban is lehetnek megjegyzések\n" +" keyword mint -k\n" +" monitor mint -m\n" +" noclobber mint -C\n" +" noexec mint -n\n" +" noglob mint -f\n" +" nolog (jelenleg nincs hatása)\n" +" notify mint -b\n" +" nounset mint -u\n" +" onecmd mint -t\n" +" physical mint -P\n" +" pipefail egy csÅ‘vezeték-parancs kilépési kódja az utolsó\n" +" nem nullával kilépett parancs kilépési kódja,\n" +" vagy 0, ha nem volt ilyen\n" +" posix a bash viselkedésének megváltoztatása, ha az\n" +" alapértelmezett nem felel meg a Posix\n" +" szabványnak\n" +" privileged mint -p\n" +" verbose mint -v\n" +" vi vi-szerű sorszerkesztés\n" +" xtrace mint -x\n" +" -p Mindig be van kapcsolva, ha a valós és effektÃv felhasználó nem\n" +" egyezik. Letiltja az $ENV fájl értelmezését és a parancsértelme-\n" +" zÅ‘-függvények betöltését. A kapcsoló kikapcsolása az effektÃv\n" +" uid és gid valósra állÃtását okozza\n" +" -t Egyetlen parancs beolvasása és végrehajtás után kilépés\n" +" -u Nem létezÅ‘ változók behelyettesÃtése legyen hiba\n" +" -v Beolvasott parancsok kiÃrása olvasáskor (értelmezés elÅ‘tt).\n" +" -x Parancsok kiÃrása végrehajtáskor (értelmezés után).\n" +" -B Szögleteszárójel-kiegészÃtés végrehajtása\n" +" -C LétezÅ‘ normál fájlok felülÃrásának tiltása kimenetátirányÃtásnál\n" +" -E Az ERR csapdát öröklik a függvények\n" +" -H Felkiáltójeles elÅ‘zményhelyettesÃtés engedélyezése. InteraktÃv\n" +" parancsértelmezÅ‘nél alapértelmezés\n" +" -P Parancsok végrehajtásánál szimbolikus linkek követésének tiltá-\n" +" sa (például cd esetében)\n" +" -T A DEBUG csapdát öröklik a függvények\n" +" - A további argumentumok pozicionális paraméterekhez rendelése\n" +" A -x és -v kapcsolók ki vannak kapcsolva.\n" +" \n" +" „-†helyett „+†használatával a kapcsolók tilthatóak. A kapcsolók a\n" +" parancsértelmezÅ‘ indÃtásakor is állÃthatóak. Az érvényben lévÅ‘ kapcso-\n" +" lók a $- változóban vannak. A záró nem értelmezhetÅ‘ argumentumok pozi-\n" +" cionális paraméterek lesznek (rendre $1, $2 ... $n). Ha nincs ARG, min-\n" +" den parancsértelmezÅ‘-változó kiÃrásra kerül.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha érvénytelen kapcsolót kap." #: builtins.c:1104 msgid "" @@ -3170,8 +3711,7 @@ msgid "" " -f\ttreat each NAME as a shell function\n" " -v\ttreat each NAME as a shell variable\n" " \n" -" Without options, unset first tries to unset a variable, and if that " -"fails,\n" +" Without options, unset first tries to unset a variable, and if that fails,\n" " tries to unset a function.\n" " \n" " Some variables cannot be unset; also see `readonly'.\n" @@ -3179,14 +3719,29 @@ msgid "" " Exit Status:\n" " Returns success unless an invalid option is given or a NAME is read-only." msgstr "" +"ParancsértelmezÅ‘-változók, -függvények és -jellemzÅ‘k törlése.\n" +" \n" +" Minden NÉV nevű függvény vagy változó törlése.\n" +" \n" +" Kapcsolók:\n" +" -f minden NÉV függvény\n" +" -v minden NÉV változó\n" +" \n" +" Kapcsolók nélkül az unset elÅ‘ször változót, sikertelenség esetén függ-\n" +" vényt próbál törölni.\n" +" \n" +" Néhány változót nem lehet törölni, lásd „readonlyâ€.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha hibás kapcsolót kap, vagy egy NÉV csak\n" +" olvasható." #: builtins.c:1124 msgid "" "Set export attribute for shell variables.\n" " \n" " Marks each NAME for automatic export to the environment of subsequently\n" -" executed commands. If VALUE is supplied, assign VALUE before " -"exporting.\n" +" executed commands. If VALUE is supplied, assign VALUE before exporting.\n" " \n" " Options:\n" " -f\trefer to shell functions\n" @@ -3198,6 +3753,21 @@ msgid "" " Exit Status:\n" " Returns success unless an invalid option is given or NAME is invalid." msgstr "" +"Exportálásra jelöl egy parancsértelmezÅ‘-változót.\n" +" \n" +" Minden NÉV automatikus környezeti változóvá exportálásra jelölése. Ãgy\n" +" minden ezután kiadott parancs környezetében megjelenik. Ha ÉRTÉK is\n" +" meg van adva, értékadás is történik.\n" +" \n" +" Kapcsolók:\n" +" -f parancsértelmezÅ‘-függvényekre vonatkozzon\n" +" -n export-jellemzÅ‘ eltávolÃtása minden NÉV-rÅ‘l\n" +" -p összes exportált változó és függvény listázása\n" +" \n" +" Egy „--†argumentum letiltja a további kapcsolóértelmezést.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha érvénytelen kapcsolót vagy NEV-et kap." #: builtins.c:1143 msgid "" @@ -3218,6 +3788,22 @@ msgid "" " Exit Status:\n" " Returns success unless an invalid option is given or NAME is invalid." msgstr "" +"ParancsértelmezÅ‘-változó változtathatatlannak jelölése.\n" +" \n" +" Minden NÉV csak olvashatóvá állÃtása. A továbbiakban a NEV-ek értéke\n" +" értékadással nem változtatható. Ha ÉRTÉK is van megadva, az Ãrásvéde-\n" +" lem bekapcsolása elÅ‘tt értékadás is történik.\n" +" \n" +" Kapcsolók:\n" +" -a indexelt tömbváltozókra vonatkozik\n" +" -A asszociatÃv tömbváltozókra vonatkozik\n" +" -f függvényekre vonatkozik\n" +" -p az összes csak olvasható változó és függvény listázása\n" +" \n" +" Egy „--†argumentum letiltja a további kapcsolóértelmezést.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha érvénytelen kapcsolót vagy NEV-et kap." #: builtins.c:1164 msgid "" @@ -3229,6 +3815,13 @@ msgid "" " Exit Status:\n" " Returns success unless N is negative or greater than $#." msgstr "" +"Pozicionális paraméterek eltolása.\n" +" \n" +" Az $N+1, $N+2... pozicionális paraméterek átnevezése $1, $2... névre.\n" +" Ha nincs N megadva, értéke 1 lesz.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha N negatÃv vagy nagyobb mint $#." #: builtins.c:1176 builtins.c:1191 msgid "" @@ -3243,6 +3836,15 @@ msgid "" " Returns the status of the last command executed in FILENAME; fails if\n" " FILENAME cannot be read." msgstr "" +"Parancsok végrehajtása fájlból a futó parancsértelmezÅ‘ben.\n" +" \n" +" FÃJLNÉV fájlból a parancsok beolvasása és végrehajtása. A fájlnév meg-\n" +" találásához a $PATH által felsorolt könyvtárakban keres. Az ARGumentu-\n" +" mok pozicionális paraméterek lesznek FÃJLNÉV végrehajtásakor.\n" +" \n" +" Kilépési kód:\n" +" Az utolsó FÃJLNÉV-beli parancs kilépési kódjával tér vissza; sikerte-\n" +" lenül, ha FÃJLNÉV nem olvasható." #: builtins.c:1207 msgid "" @@ -3257,6 +3859,18 @@ msgid "" " Exit Status:\n" " Returns success unless job control is not enabled or an error occurs." msgstr "" +"Parancsvégrehajtás felfüggesztése.\n" +" \n" +" A futó parancsértelmezÅ‘ végrehajtásának felfüggesztése SIGCONT szignál\n" +" érkezéséig. Ha nincs erÅ‘ltetve, bejelentkezÅ‘ parancsértelmezÅ‘t nem\n" +" függeszthetÅ‘ fel.\n" +" \n" +" Kapcsolók:\n" +" -f felfüggesztés erÅ‘ltetése bejelentkezÅ‘ parancsértelmezÅ‘n is\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha a munkakezelés nem támogatott vagy hiba\n" +" történt." #: builtins.c:1223 msgid "" @@ -3289,8 +3903,7 @@ msgid "" " -x FILE True if the file is executable by you.\n" " -O FILE True if the file is effectively owned by you.\n" " -G FILE True if the file is effectively owned by your group.\n" -" -N FILE True if the file has been modified since it was last " -"read.\n" +" -N FILE True if the file has been modified since it was last read.\n" " \n" " FILE1 -nt FILE2 True if file1 is newer than file2 (according to\n" " modification date).\n" @@ -3311,8 +3924,7 @@ msgid "" " STRING1 != STRING2\n" " True if the strings are not equal.\n" " STRING1 < STRING2\n" -" True if STRING1 sorts before STRING2 " -"lexicographically.\n" +" True if STRING1 sorts before STRING2 lexicographically.\n" " STRING1 > STRING2\n" " True if STRING1 sorts after STRING2 lexicographically.\n" " \n" @@ -3334,35 +3946,113 @@ msgid "" " Returns success if EXPR evaluates to true; fails if EXPR evaluates to\n" " false or an invalid argument is given." msgstr "" +"Feltételes kifejezések kiértékelése.\n" +" \n" +" 0-val (igaz) vagy 1-gyel (hamis) lép ki a KIFejezés értékétÅ‘l függÅ‘en.\n" +" A kifejezéseknek egy vagy két operandusa lehet. Az egyoperandusú kife-\n" +" jezések többnyire fájlok állapotát vizsgálják. Karakterláncokat és\n" +" számokat is lehet összehasonlÃtani.\n" +" \n" +" Fájl-operátorok:\n" +" \n" +" -a FÃJL Igaz, ha a fájl létezik.\n" +" -b FÃJL Igaz, ha a fájl blokkeszköz.\n" +" -c FÃJL Igaz, ha a fájl karakteres eszköz.\n" +" -d FÃJL Igaz, ha a fájl könyvtár.\n" +" -e FÃJL Igaz, ha a fájl létezik.\n" +" -f FÃJL Igaz, ha a fájl létezik és normál fájl.\n" +" -g FÃJL Igaz, ha a fájl SETGID jogosultságú.\n" +" -h FÃJL Igaz, ha a fájl szimbolikus link.\n" +" -L FÃJL Igaz, ha a fájl szimbolikus link.\n" +" -k FÃJL Igaz, ha a fájlnak „sticky†bitje (t-bitje) van.\n" +" -p FÃJL Igaz, ha a fájl egy elnevezett csÅ‘vezeték.\n" +" -r FÃJL Igaz, ha a fájl olvasható a felhasználó számára.\n" +" -s FÃJL Igaz, ha a fájl létezik és nem üres.\n" +" -S FÃJL Igaz, ha a fájl egy foglalat (socket).\n" +" -t FD Igaz, ha a fájlleÃró egy terminál.\n" +" -u FÃJL Igaz, ha a fájl SETUID jogosultságú.\n" +" -w FÃJL Igaz, ha a fájl Ãrható a felhasználó számára.\n" +" -x FÃJL Igaz, ha a fájl végrehajtható a felhasználó számára.\n" +" -O FÃJL Igaz, ha a fájl effektÃven a felhasználó tulajdona.\n" +" -G FÃJL Igaz, ha a fájl effektÃven a csoport tulajdona.\n" +" -N FÃJL Igaz, ha a fájl módosult utolsó olvasása óta.\n" +" \n" +" FÃJL1 -nt FÃJL2 Igaz, ha fájl1 újabb fájl2-nél (a módosÃtási dátum\n" +" alapján).\n" +" \n" +" FÃJL1 -ot FÃJL2 Igaz, ha fájl1 régebbi fájl2-nél.\n" +" \n" +" FÃJL1 -ef FÃJL2 Igaz, ha fájl1 hard link fájl2-re.\n" +" \n" +" Karakterlánc-operátorok:\n" +" \n" +" -z SZÖVEG Igaz, ha SZÖVEG üres.\n" +" \n" +" -n SZÖVEG Igaz, ha SZÖVEG nem üres.\n" +" \n" +" SZÖVEG1 = SZÖVEG2\n" +" Igaz, ha a két SZÖVEG egyezik.\n" +" SZÖVEG1 != SZÖVEG2\n" +" Igaz, ha a két SZÖVEG nem egyezik.\n" +" SZÖVEG1 < SZÖVEG2\n" +" Igaz, ha SZÖVEG1 SZÖVEG2 elÅ‘tt van a betűrendben.\n" +" SZÖVEG1 > SZÖVEG2\n" +" Igaz, ha SZÖVEG1 SZÖVEG2 után van a betűrendben.\n" +" \n" +" További operátorok:\n" +" \n" +" -o BEÃLLÃTÃS Igaz, ha a parancsértelmezÅ‘-beállÃtás engedélyezve\n" +" van.\n" +" ! KIF Igaz, ha kif hamis.\n" +" KIF1 -a KIF2 Igaz, ha kif1 ÉS kif2 is igaz.\n" +" KIF1 -o KIF2 Igaz, ha kif1 VAGY kif2 igaz.\n" +" \n" +" arg1 OP arg2 Aritmetikai összehasonlÃtások. OP lehet: -eq, -ne,\n" +" -lt, -le, -gt vagy -ge.\n" +" \n" +" A kétoperandusú aritmetikai operátorok igazat adnak, ha ARG1 rendre\n" +" egyenlÅ‘, nem egyenlÅ‘, kisebb, kisebb vagy egyenlÅ‘, nagyobb, nagyobb\n" +" vagy egyenlÅ‘, mint ARG2.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, ha KIF igaz; sikertelenséggel, ha KIF hamis vagy\n" +" érvénytelen argumentumokat kap." #: builtins.c:1299 -#, fuzzy msgid "" "Evaluate conditional expression.\n" " \n" " This is a synonym for the \"test\" builtin, but the last argument must\n" " be a literal `]', to match the opening `['." msgstr "" -"egy ']' szövegkonstansnak kell lennie, hogy párban legyen a nyitó '['-val." +"Feltételes kifejezések kiértékelése.\n" +" \n" +" Ez a „test†beépÃtett parancs szinonimája, de annyiban eltér tÅ‘le,\n" +" hogy az utolsó argumentuma „]†kell legyen – a nyitó „]â€-lel összhang-\n" +" ban." #: builtins.c:1308 msgid "" "Display process times.\n" " \n" -" Prints the accumulated user and system times for the shell and all of " -"its\n" +" Prints the accumulated user and system times for the shell and all of its\n" " child processes.\n" " \n" " Exit Status:\n" " Always succeeds." msgstr "" +"Végrehajtási idÅ‘k kiÃrása.\n" +" \n" +" MegjelenÃti a kumulált felhasználói- és rendszergépidÅ‘t, amelyet a pa-\n" +" rancsértelmezÅ‘ és gyermekfolyamatai használtak. \n" +" Kilépési kód:\n" +" Mindig sikeres." #: builtins.c:1320 msgid "" "Trap signals and other events.\n" " \n" -" Defines and activates handlers to be run when the shell receives " -"signals\n" +" Defines and activates handlers to be run when the shell receives signals\n" " or other conditions.\n" " \n" " ARG is a command to be read and executed when the shell receives the\n" @@ -3371,27 +4061,51 @@ msgid "" " value. If ARG is the null string each SIGNAL_SPEC is ignored by the\n" " shell and by the commands it invokes.\n" " \n" -" If a SIGNAL_SPEC is EXIT (0) ARG is executed on exit from the shell. " -"If\n" +" If a SIGNAL_SPEC is EXIT (0) ARG is executed on exit from the shell. If\n" " a SIGNAL_SPEC is DEBUG, ARG is executed before every simple command.\n" " \n" -" If no arguments are supplied, trap prints the list of commands " -"associated\n" +" If no arguments are supplied, trap prints the list of commands associated\n" " with each signal.\n" " \n" " Options:\n" " -l\tprint a list of signal names and their corresponding numbers\n" " -p\tdisplay the trap commands associated with each SIGNAL_SPEC\n" " \n" -" Each SIGNAL_SPEC is either a signal name in <signal.h> or a signal " -"number.\n" +" Each SIGNAL_SPEC is either a signal name in <signal.h> or a signal number.\n" " Signal names are case insensitive and the SIG prefix is optional. A\n" " signal may be sent to the shell with \"kill -signal $$\".\n" " \n" " Exit Status:\n" -" Returns success unless a SIGSPEC is invalid or an invalid option is " -"given." +" Returns success unless a SIGSPEC is invalid or an invalid option is given." msgstr "" +"Szignálok és más események elfogása.\n" +" \n" +" Meghatároz és aktivál eseménykezelÅ‘ket, amelyek szignálok és más kör-\n" +" rülmények bekövetkezésekor futnak.\n" +" \n" +" ARG az a parancs, amelyet a parancsértelmezÅ‘ beolvas és végrehajt a\n" +" SZIGNÃL(ok) bekövetkezésekor. Ha ARG hiányzik (és egy SZIGNÃL van meg-\n" +" adva) vagy ARG egy „-â€, akkor minden szignálkezelÅ‘ visszaáll az alap-\n" +" értelmezett viselkedésre. Ha ARG üres, akkor a megadott SZIGNÃL-ok be-\n" +" következésekor nem történik semmi a parancsértelmezÅ‘ben és új gyermek-\n" +" folyamataiban.\n" +" \n" +" Ha a SZIGNÃL értéke EXIT (0), ARG a parancsértelmezÅ‘bÅ‘l való kilépéskor\n" +" fut. Ha értéke DEBUG, ARG minden parancs elÅ‘tt fut. Ha nincsenek argu-\n" +" mentumok, a trap kilistázza az összes szignált és parancsot.\n" +" \n" +" Kapcsolók:\n" +" -l a rendszeren érvényes szignálnevek és sorszámaik kilistázása\n" +" -p kilistázza a trap által beállÃtott eseménykezelÅ‘ket\n" +" \n" +" SZIGNÃL értéke egy trap -l által kilistázott szignálnév vagy szám.\n" +" A szignálnevek kis- és nagybetűkre érzéketlenek, a SIG elÅ‘tag elhagy-\n" +" ható. Szignált a parancsértelmezÅ‘nek a „kill -szignál $$†paranccsal\n" +" lehet küldeni.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha SZIGNÃL érvénytelen vagy érvénytelen\n" +" kapcsolót kap." #: builtins.c:1352 msgid "" @@ -3419,16 +4133,36 @@ msgid "" " NAME\tCommand name to be interpreted.\n" " \n" " Exit Status:\n" -" Returns success if all of the NAMEs are found; fails if any are not " -"found." +" Returns success if all of the NAMEs are found; fails if any are not found." msgstr "" +"Tájékoztat egy parancs tÃpusáról.\n" +" \n" +" Minden NÉV-ra kiÃrja, hogy hogy lenne értelmezve parancsnévként.\n" +" \n" +" Kapcsolók:\n" +" -a minden NÉV-re illeszkedÅ‘ futtatható parancs felsorolása,\n" +" beleértve az aliasokat, beépÃtett parancsokat, és a függvé-\n" +" nyeket (ha „-p†nem tiltja)\n" +" -f függvényeket ne keressen\n" +" -P csak a PATH-ban keresse NEV-et, akkor is, ha van ilyen nevű\n" +" alias, parancs vagy függvény\n" +" -p a végrehajtható fájl nevét Ãrja ki, ha amely végrehajtódna\n" +" a parancs kiadásakor. Ha ez nem fájl lenne, nem Ãr ki semmit\n" +" -t egyetlen szót Ãr ki, amely NÉV tÃpusát jelzi: „aliasâ€,\n" +" „keyword†(kulcsszó), „function†(függvény), „builtin†(be-\n" +" épÃtett parancs), „file†(fájl) vagy „†(nem található)\n" +" \n" +" Kapcsolók:\n" +" NÉV ÉrtelmezendÅ‘ parancsnév.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel lép ki, ha minden NÉV megtalálható, sikertelenül, ha nem." #: builtins.c:1383 msgid "" "Modify shell resource limits.\n" " \n" -" Provides control over the resources available to the shell and " -"processes\n" +" Provides control over the resources available to the shell and processes\n" " it creates, on systems that allow such control.\n" " \n" " Options:\n" @@ -3466,6 +4200,45 @@ msgid "" " Exit Status:\n" " Returns success unless an invalid option is supplied or an error occurs." msgstr "" +"ParancsértelmezÅ‘ erÅ‘forráskorlátjainak beállÃtása.\n" +" \n" +" Szabályozási lehetÅ‘séget ad a parancsértelmezÅ‘ által elérhetÅ‘ erÅ‘for-\n" +" rások korlátozásához, ha a rendszer támogatja.\n" +" \n" +" Kapcsolók:\n" +" -S a puha (soft) korlátozás használata\n" +" -H a kemény (hard) korlátozás használata\n" +" -a az összes aktuális korlátozás kilistázása\n" +" -b foglalatok (socket) puffermérete\n" +" -c core fájlok maximális mérete (0 tiltja)\n" +" -d folyamatok maximális adatszegmens-mérete\n" +" -e a maximális ütemezési prioritás (nice)\n" +" -f a parancsértelmezÅ‘ és gyermekei által Ãrható legnagyobb fájl\n" +" -i várakozó szignálok maximális száma\n" +" -l folyamatonként foglalható memória maximális mérete\n" +" -m a maximálisan operatÃv memóriában tartható terület mérete\n" +" -n nyitott fájlleÃrók maximális száma\n" +" -p a csÅ‘vezetékpuffer mérete\n" +" -q a Posix üzenetsorokban tartható byte-ok száma\n" +" -r a maximális valós idejű ütemezési prioritás\n" +" -s maximális veremméret\n" +" -t maximális processzoridÅ‘ másodpercekben\n" +" -u felhasználói folyamatok maximális száma\n" +" -v virtuális memória mérete\n" +" -x fájlzárolások maximális száma\n" +" \n" +" Ha KORLÃT meg van adva, az lesz az új értéke a megadott erÅ‘forrásnak.\n" +" Speciális KORLÃT-értékek: „hard†(jelenlegi kemény korlát értéke),\n" +" „soft†(jelenlegi puha korlát értéke) és „unlimited†(korlátozás nél-\n" +" kül).\n" +" Ha nincs kapcsoló megadva, -f az alapértelmezett.\n" +" \n" +" Az értékek 1024 byte-os egységekben értendÅ‘ek, kivéve a -t, amely má-\n" +" sodpercekben, a -p, amely 512 byte-okban, valamint a -u, amely darab-\n" +" ban értendÅ‘.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve érvénytelen kapcsoló és hiba esetében." #: builtins.c:1428 msgid "" @@ -3484,6 +4257,23 @@ msgid "" " Exit Status:\n" " Returns success unless MODE is invalid or an invalid option is given." msgstr "" +"KiÃrja vagy beállÃtja a fájlmódmaszkot.\n" +" \n" +" BeállÃtja a fájllétrehozási maszkot MÓD-ra. Ha MÓD hiányzik, az aktuá-\n" +" lis értékét Ãrja ki.\n" +" Fájlok létrehozásakor az alapértelmezett jogokból ki lesznek maszkolva\n" +" az itt megadott bitek. Ez nem akadályozza meg, hogy a program vagy a\n" +" felhasználó késÅ‘bb megváltoztassa a fájl jogait.\n" +" \n" +" Ha MÓD számjeggyel kezdÅ‘dik, oktális számként lesz értelmezve; egyéb-\n" +" ként a chmod(1) által használt szimbolikus formátumban.\n" +" \n" +" Kapcsolók:\n" +" -p ha MÓD hiányzik, a kimenet újrahasználó formátumot használjon\n" +" -S a kimenet használja a szimbolikus formát (különben oktálisat)\n" +" \n" +" Kilépési kód:\n" +" Sikerrel lép ki, kivéve ha MÓD vagy egy kapcsoló érvénytelen." #: builtins.c:1448 msgid "" @@ -3492,15 +4282,23 @@ msgid "" " Waits for the process identified by ID, which may be a process ID or a\n" " job specification, and reports its termination status. If ID is not\n" " given, waits for all currently active child processes, and the return\n" -" status is zero. If ID is a a job specification, waits for all " -"processes\n" +" status is zero. If ID is a a job specification, waits for all processes\n" " in the job's pipeline.\n" " \n" " Exit Status:\n" -" Returns the status of ID; fails if ID is invalid or an invalid option " -"is\n" +" Returns the status of ID; fails if ID is invalid or an invalid option is\n" " given." msgstr "" +"Munka befejezésének megvárása és a kilépési kód visszaadása.\n" +" \n" +" ID számú folyamat befejezésére vár, majd jelzi a kilépési kódját. ID\n" +" lehet egy PID vagy egy %MUNKASZÃM. Ha nincs ID megadva, bármelyik\n" +" gyermekfolyamat befejezésekor visszatér, 0-val. Ha ID munkaszám, a\n" +" csÅ‘vezeték összes folyamatát bevárja.\n" +" \n" +" Kilépési kód:\n" +" ID kilépési kódjával tér vissza; érvénytelen ID vagy kapcsoló esetén\n" +" sikertelenül." #: builtins.c:1466 msgid "" @@ -3511,10 +4309,17 @@ msgid "" " and the return code is zero. PID must be a process ID.\n" " \n" " Exit Status:\n" -" Returns the status of ID; fails if ID is invalid or an invalid option " -"is\n" +" Returns the status of ID; fails if ID is invalid or an invalid option is\n" " given." msgstr "" +"Munka befejezésének megvárása és a kilépési kód visszaadása.\n" +" \n" +" ID számú folyamat befejezésére vár, majd jelzi a kilépési kódját. ID\n" +" egy folyamatazonosÃtó kell legyen.\n" +" \n" +" Kilépési kód:\n" +" ID kilépési kódjával tér vissza; érvénytelen ID vagy kapcsoló esetén\n" +" sikertelenül." #: builtins.c:1481 msgid "" @@ -3528,6 +4333,15 @@ msgid "" " Exit Status:\n" " Returns the status of the last command executed." msgstr "" +"Egy lista minden elemére parancs végrehajtása.\n" +" \n" +" A „for†ciklus végrehajt egy parancssorozatot a megadott listán. Ha az\n" +" „in SZAVAK ...;†rész hiányzik, „in \"$@\"†az alapértelmezés. Minden\n" +" iterációnál NÉV értéke a SZAVAK lista megfelelÅ‘ elemére lesz állÃtva,\n" +" és Ãgy futnak a PARANCSOK.\n" +" \n" +" Kilépési kód:\n" +" Az utolsó parancs kilépési kódját adja vissza." #: builtins.c:1495 msgid "" @@ -3545,6 +4359,19 @@ msgid "" " Exit Status:\n" " Returns the status of the last command executed." msgstr "" +"Aritmetikai for-ciklus.\n" +" \n" +" Ekvivalens a következÅ‘vel:\n" +" (( KIF1 ))\n" +" while (( KIF2 )); do\n" +" PARANCSOK\n" +" (( KIF3 ))\n" +" done\n" +" KIF1, KIF2 és KIF3 aritmetikai kifejezések. Ha valamelyik el van hagy-\n" +" va, úgy működik, mintha értéke 1 lenne.\n" +" \n" +" Kilépési kód:\n" +" Az utolsó parancs kilépési kódját adja." #: builtins.c:1513 msgid "" @@ -3565,6 +4392,22 @@ msgid "" " Exit Status:\n" " Returns the status of the last command executed." msgstr "" +"A felhasználóval kiválasztat a listából egy elemet és végrehajt rá egy pa-\n" +"rancsot.\n" +" \n" +" A SZAVAK kiértékelésre kerülnek és egy szólistát képeznek. A szavak a\n" +" szabványos hibakimenetre kerülnek soronként, sorszámozva. Ezután meg-\n" +" jelenik a $PS3 és egy sorszámot vár a szabványos bemeneten. Érvényes\n" +" sorszám megadásakor a PARANCSOKAT végrehajtja úgy, hogy NÉV a megfelelÅ‘\n" +" sorszámú elem értékét kapja. Ezután újból megjelenik $PS3 és újból le-\n" +" het választani. EOF bemenet és break parancs esetén fejezÅ‘dik be a hu-\n" +" rok. Érvénytelen választás esetén szintén új prompt jelenik meg. Ãœres\n" +" sor beolvasásakor a lehetÅ‘ségek is újra megjelennek. A beolvasott sor\n" +" a REPLY változóba kerül. Ha elmarad az „in SZAVAK†rész, „in \"$@\"â€\n" +" az alapértelmezés.\n" +" \n" +" Kilépési kód:\n" +" Az utolsó parancs kilépési kódját adja vissza." #: builtins.c:1534 msgid "" @@ -3581,9 +4424,22 @@ msgid "" " Exit Status:\n" " The return status is the return status of PIPELINE." msgstr "" +"A csÅ‘vezeték végrehajtási idejét Ãrja ki.\n" +" \n" +" CSÅVEZETÉK végrehajtása és egy összefoglaló kiÃrása a végrehajtás köz-\n" +" ben eltelt valós idÅ‘rÅ‘l, a használt felhasználói- és rendszergépidÅ‘k-\n" +" rÅ‘l CSÅVEZETÉK befejezÅ‘désekor.\n" +" \n" +" Kapcsolók:\n" +" -p az összefoglaló megjelenÃtése a hordozható Posix formában\n" +" \n" +" A TIMEFORMAT változó értéke felhasználásra kerül a kimenet formázása-\n" +" kor.\n" +" \n" +" Kilépési kód:\n" +" A kilépési kód a CSÅVEZETÉK kilépési kódja lesz." #: builtins.c:1551 -#, fuzzy msgid "" "Execute commands based on pattern matching.\n" " \n" @@ -3592,31 +4448,45 @@ msgid "" " \n" " Exit Status:\n" " Returns the status of the last command executed." -msgstr "Feltételesen futtatja a PARANCSOT ha a SZÓ megegyezik a MINTÁVAL. A" +msgstr "" +"Parancsok végrehajtása mintaillesztés alapján.\n" +" \n" +" PARANCSOK végrehajtása azon SZAVAKon, amelyek illeszkednek a MINTÃ-ra.\n" +" Több mintát „|†jellel lehet elválasztani. A minták a fájlnév-helyet-\n" +" tesÃtés formátumát használják.\n" +" \n" +" Kilépési kód:\n" +" Az utolsó parancs kilépési kódját adja vissza." #: builtins.c:1563 msgid "" "Execute commands based on conditional.\n" " \n" -" The `if COMMANDS' list is executed. If its exit status is zero, then " -"the\n" -" `then COMMANDS' list is executed. Otherwise, each `elif COMMANDS' list " -"is\n" +" The `if COMMANDS' list is executed. If its exit status is zero, then the\n" +" `then COMMANDS' list is executed. Otherwise, each `elif COMMANDS' list is\n" " executed in turn, and if its exit status is zero, the corresponding\n" -" `then COMMANDS' list is executed and the if command completes. " -"Otherwise,\n" -" the `else COMMANDS' list is executed, if present. The exit status of " -"the\n" -" entire construct is the exit status of the last command executed, or " -"zero\n" +" `then COMMANDS' list is executed and the if command completes. Otherwise,\n" +" the `else COMMANDS' list is executed, if present. The exit status of the\n" +" entire construct is the exit status of the last command executed, or zero\n" " if no condition tested true.\n" " \n" " Exit Status:\n" " Returns the status of the last command executed." msgstr "" +"Parancsok végrehajtása feltételesen.\n" +" \n" +" Az „if PARANCSOK†lista végrehajtásra kerül. Ha kilépési kódja nulla,\n" +" akkor a „then PARANCSOK†lista kerül végrehajtásra. Ha nem, akkor az\n" +" elsÅ‘ nullával kilépÅ‘ „elif PARANCSOK†listához tartozó „then PARARAN-\n" +" CSOK†lista kerül végrehajtásra. Ha egyik sem teljesül, az „else PA-\n" +" RANCSOK†lista kerül végrehajtásra. Az egész szerkezet kilépési kódja\n" +" az utoljára végrehajtott parancs kilépési kódja, vagy nulla, ha nem\n" +" teljesült egyik feltétel sem.\n" +" \n" +" Kilépési kód:\n" +" Az utoljára végrehajtott parancs kilépési kódja." #: builtins.c:1580 -#, fuzzy msgid "" "Execute commands as long as a test succeeds.\n" " \n" @@ -3625,10 +4495,16 @@ msgid "" " \n" " Exit Status:\n" " Returns the status of the last command executed." -msgstr "Kibontja és végrehajtja a PARANCSOT amíg az utolsó parancs a " +msgstr "" +"Parancsok végrehajtása amÃg a feltétel teljesül.\n" +" \n" +" PARANCSOK végrehajtása addig, amÃg a „while PARANCSOK†utolsó paran-\n" +" csa nullával lép ki.\n" +" \n" +" Kilépési kód:\n" +" Az utolsónak végrehajtott parancs kilépési kódja." #: builtins.c:1592 -#, fuzzy msgid "" "Execute commands as long as a test does not succeed.\n" " \n" @@ -3637,7 +4513,14 @@ msgid "" " \n" " Exit Status:\n" " Returns the status of the last command executed." -msgstr "Kibontja és végrehajtja a PARANCSOT amíg az utolsó parancs a " +msgstr "" +"Parancsok végrehajtása amÃg a feltétel nem teljesül.\n" +" \n" +" PARANCSOK végrehajtása addig, amÃg a „until PARANCSOK†utolsó paran-\n" +" csa nem nullával lép ki.\n" +" \n" +" Kilépési kód:\n" +" Az utolsónak végrehajtott parancs kilépési kódja." #: builtins.c:1604 msgid "" @@ -3651,23 +4534,39 @@ msgid "" " Exit Status:\n" " Returns the exit status of COMMAND." msgstr "" +"Egy NÉV nevű társfolyamat létrehozása.\n" +" \n" +" PARANCS aszinkron végrehajtása, a szabványos ki- és bemenet átirányÃ-\n" +" tásával egy-egy csÅ‘vezetékbe, amelyek fájlleÃrói a NÉV tömb 0-s és 1-\n" +" es elemeibe kerülnek a végrehajtó parancsértelmezÅ‘ben. Az alapértelme-\n" +" zett név: „COPROCâ€.\n" +" \n" +" Kilépési kód:\n" +" A PARANCS kilépési kódjával tér vissza." #: builtins.c:1618 msgid "" "Define shell function.\n" " \n" " Create a shell function named NAME. When invoked as a simple command,\n" -" NAME runs COMMANDs in the calling shell's context. When NAME is " -"invoked,\n" +" NAME runs COMMANDs in the calling shell's context. When NAME is invoked,\n" " the arguments are passed to the function as $1...$n, and the function's\n" " name is in $FUNCNAME.\n" " \n" " Exit Status:\n" " Returns success unless NAME is readonly." msgstr "" +"ParancsértelmezÅ‘-függvény definiálása.\n" +" \n" +" Létrehoz egy NÉV nevű függvényt. Ha NÉV parancsként végrehajtásra ke-\n" +" rül, PARANCSOK futnak a hÃvó parancsértelmezÅ‘ környezetében. NÉV hÃvá-\n" +" sakor az argumentumok a függvénybÅ‘l $1...$n néven érhetÅ‘ek el, mÃg a\n" +" függvény neve $FUNCNAME-ként.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha NÉV csak olvasható." #: builtins.c:1632 -#, fuzzy msgid "" "Group commands as a unit.\n" " \n" @@ -3676,7 +4575,14 @@ msgid "" " \n" " Exit Status:\n" " Returns the status of the last command executed." -msgstr "Parancsok halmazát futtatja egy csoportban. Ez az egyik módja az" +msgstr "" +"Parancsok csoportosÃtása egy egységgé.\n" +" \n" +" Egy csoportként hajt végre egy parancssorozatot. Ez egy módja a ki- és\n" +" bemenetek parancshalmazba való átirányÃtásának.\n" +" \n" +" Kilépési kód:\n" +" Az utolsó parancs kilépési kódját adja vissza." #: builtins.c:1644 msgid "" @@ -3691,6 +4597,14 @@ msgid "" " Exit Status:\n" " Returns the status of the resumed job." msgstr "" +"Egy munkát elÅ‘térbe hoz.\n" +" \n" +" Megegyezik az „fg†parancs MUNKASZÃM argumentumával. Egy megszakÃtott\n" +" vagy háttérben futó munkát hoz elÅ‘térbe. MUNKASZÃM lehet munkanév vagy\n" +" munkaazonosÃtó is. Egy záró „&†megadása a munkát háttérbe küldi, mint\n" +" a „bg†parancs. \n" +" Kilépési kód:\n" +" A visszaállÃtott parancs kilépési kódjával lép ki." #: builtins.c:1659 msgid "" @@ -3702,17 +4616,21 @@ msgid "" " Exit Status:\n" " Returns 1 if EXPRESSION evaluates to 0; returns 0 otherwise." msgstr "" +"Aritmetikai kifejezéseket értékel ki.\n" +" \n" +" A KIFEJEZÉS az aritmetikai kiértékelés szabályai szerint kerülnek ki-\n" +" értékelésre. Megyegyezik a „let KIFEJEZÉS†paranccsal.\n" +" \n" +" Kilépési kód:\n" +" 1-gyel tér vissza, ha KIFEJEZÉS értéke 0, különben 0-val." #: builtins.c:1671 msgid "" "Execute conditional command.\n" " \n" -" Returns a status of 0 or 1 depending on the evaluation of the " -"conditional\n" -" expression EXPRESSION. Expressions are composed of the same primaries " -"used\n" -" by the `test' builtin, and may be combined using the following " -"operators:\n" +" Returns a status of 0 or 1 depending on the evaluation of the conditional\n" +" expression EXPRESSION. Expressions are composed of the same primaries used\n" +" by the `test' builtin, and may be combined using the following operators:\n" " \n" " ( EXPRESSION )\tReturns the value of EXPRESSION\n" " ! EXPRESSION\t\tTrue if EXPRESSION is false; else false\n" @@ -3730,6 +4648,27 @@ msgid "" " Exit Status:\n" " 0 or 1 depending on value of EXPRESSION." msgstr "" +"Feltételes parancs végrehajtása.\n" +" \n" +" 0 vagy 1 kilépési kódot ad a KIFEJEZÉS kiértékelésének eredményétÅ‘l\n" +" függÅ‘en. A kifejezések a „test†parancs által használt primitÃvekbÅ‘l\n" +" épülnek fel, és a következÅ‘ operátorokkal keverhetÅ‘ek.\n" +" \n" +" ( KIFEJEZÉS ) KIFEJEZÉS értékét adja vissza\n" +" ! KIFEJEZÉS Igaz, ha KIFEJEZÉS hamis\n" +" KIF1 && KIF2 Igaz, ha KIF1 és KIF2 is igaz\n" +" KIF1 || KIF2 Igaz, ha KIF1 vagy KIF2 igaz\n" +" \n" +" Az „==†és „!=†operátorok használatánál a jobbérték mintaként értel-\n" +" mezÅ‘dik, és fájlnévillesztés történik. A hasonlóan működÅ‘ „=~†operá-\n" +" tor használatakor a jobbérték reguláris kifejezésként kerül illesztés-\n" +" re.\n" +" \n" +" Az „&&†és „||†operátorok rövidzár-tulajdonságúak, vagyis KIF2-t nem\n" +" értékelik ki, ha KIF1-bÅ‘l is megállapÃtható a kifejezés értéke.\n" +" \n" +" Kilépési kód:\n" +" 0 vagy 1 a KIFEJEZÉS-tÅ‘l függÅ‘en." #: builtins.c:1697 msgid "" @@ -3784,6 +4723,53 @@ msgid "" " HISTIGNORE\tA colon-separated list of patterns used to decide which\n" " \t\tcommands should be saved on the history list.\n" msgstr "" +"Közös parancsértelmezÅ‘-változók és használatuk.\n" +" \n" +" BASH_VERSION Verzióadatok errÅ‘l a Bash-rÅ‘l\n" +" CDPATH KettÅ‘spontokkal elválasztott könyvtárlista, amelyekben a\n" +" „cd†keres\n" +" GLOBIGNORE KettÅ‘spontokkal elválasztott mintalista, amelyekre illesz-\n" +" kedÅ‘ nevű fájlok nem kerülnek útvonal-kiegészÃtésre\n" +" HISTFILE A parancselÅ‘zményeket tároló fájl neve\n" +" HISTFILESIZE Az elÅ‘zményfájl maximális hossza sorokban\n" +" HISTSIZE A parancsértelmezÅ‘ által kezelt elÅ‘zménysorok maximális\n" +" száma\n" +" HOME A saját könyvtár teljes abszolút útvonala\n" +" HOSTNAME A parancsértelmezÅ‘t futtató gép neve\n" +" HOSTTYPE A Bash-t futtató CPU tÃpusa\n" +" IGNOREEOF A parancsértelmezÅ‘ viselkedését állÃtja, hogy mit tegyen,\n" +" ha egy sor elején EOF karaktert kap bemenetén. Ha ez a vál-\n" +" tozó létezik, az értékében megadott számú EOF karaktert nem\n" +" vesz figyelembe (alapértelmezetten 10). Ha nincs beállÃtva,\n" +" EOF-ra kilép a parancsértelmezÅ‘\n" +" MACHTYPE A Bash-t futtató gépet leÃró karakterlánc\n" +" MAILCHECK Megadott számú másodpercenként keres a Bash új leveleket\n" +" MAILPATH KettÅ‘spontokkal elválasztott fájlnévlista, ahol a Bash\n" +" új leveleket keres\n" +" OSTYPE A Bash-t futtató gépen futó UNIX-változat neve (verziója)\n" +" PATH KettÅ‘spontokkal elválasztott könyvtárlista, amelyekben a\n" +" Bash futtatható programokat keres parancsvégrehajtáskor\n" +" PROMPT_COMMAND Az elsÅ‘dleges prompt kiÃrása elÅ‘tt végrehajtandó pa-\n" +" rancs\n" +" PS1 Az elsÅ‘dleges prompt\n" +" PS2 A másodlagos prompt\n" +" PWD Az aktuális könyvtár teljes útvonala\n" +" SHELLOPTS Az engedélyezett shell-beállÃtások kettÅ‘spontokkal elválasz-\n" +" tott listája\n" +" TERM Az aktuális termináltÃpus neve\n" +" TIMEFORMAT A „time†parancs által használt idÅ‘formátum\n" +" auto_resume Nem üres érték esetén egy egy szóból álló parancs elÅ‘-\n" +" ször a megszakÃtott munkák nevei között lesz keresve. Talá-\n" +" lat esetén a munka elÅ‘térbe kerül. „exact†érték esetén\n" +" pontosan megegyezÅ‘ nevet keres, „substring†esetén tetszÅ‘-\n" +" leges egyezést, minden más érték esetén a szó elején keres\n" +" histchars ElÅ‘zménykiegészÃtést és gyors cserét vezérlÅ‘ karaktereket\n" +" ad meg. Az elsÅ‘ karakter az elÅ‘zménybehelyettesÃtÅ‘ karak-\n" +" ter (általában „!â€), a második a gyorscsere-karakter (álta-\n" +" lában „^â€), a harmadik pedig az elÅ‘zménymegjegyzés (általá-\n" +" ban „#â€)\n" +" HISTIGNORE KettÅ‘spontokkal elválasztott mintalista, amely mintákra\n" +" illeszkedÅ‘ parancsok nem kerülnek az elÅ‘zmények közé\n" #: builtins.c:1754 msgid "" @@ -3815,6 +4801,32 @@ msgid "" " Returns success unless an invalid argument is supplied or the directory\n" " change fails." msgstr "" +"Könyvtárakat tesz a verembe.\n" +" \n" +" Egy könyvtárat tesz a könyvtárverem tetejére, vagy forgatja a vermet,\n" +" az új felsÅ‘ elemmé a jelenlegi munkakönyvtárat téve. Argumentumok nél-\n" +" kül hÃvva a két felsÅ‘ könyvtárat cseréli meg.\n" +" \n" +" Kapcsolók:\n" +" -n Ne váltson könyvtárat hozzáadáskor, vagyis csak a\n" +" vermet változtassa.\n" +" \n" +" Argumentumok:\n" +" +N Úgy forgatja a vermet, hogy az N-edik könyvtár (0-tól\n" +" kezdve, a „dirs†által kiÃrt listán balról számolva)\n" +" kerüljön a verem tetejére.\n" +" \n" +" -N Úgy forgatja a vermet, hogy az N-edik könyvtár (0-tól\n" +" kezdve, a „dirs†által kiÃrt listán jobbról számolva)\n" +" kerüljön a verem tetejére.\n" +" \n" +" dir A verem tetejére helyezi KTÃR könyvtárat, és ugyanezt\n" +" állÃtja be új munkakönyvtárnak.\n" +" \n" +" A „dirs†beépÃtett parancs listázza a könyvtárvermet. \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve érvénytelen argumentum vagy könyvtárváltás\n" +" során történÅ‘ hiba esetén." #: builtins.c:1788 msgid "" @@ -3842,6 +4854,27 @@ msgid "" " Returns success unless an invalid argument is supplied or the directory\n" " change fails." msgstr "" +"Elemek eltávolÃtása a verembÅ‘l.\n" +" \n" +" Elemeket vesz ki a könyvtárverembÅ‘l. Argumentumok nélkül kiveszi a \n" +" legfelsÅ‘ elemet, és a kivett elemre állÃtja az új munkakönyvtárat.\n" +" \n" +" Kapcsolók:\n" +" -n Ne váltson könyvtárat eltávolÃtáskor, vagyis csak a vermet\n" +" változtassa.\n" +" \n" +" Argumentumok:\n" +" +N EltávolÃtja az N-edik elemet a „dirs†által kiÃrt listán, nullá-\n" +" tól, balról számolva. Pl. a „popd +0†az elsÅ‘, mÃg a „popd +1†a\n" +" könyvtárat távolÃtja el.\n" +" -N EltávolÃtja az N-edik elemet a „dirs†által kiÃrt listán, nullá-\n" +" tól, jobbról számolva. Pl. a „popd -0†az utolsó, a „popd -1†az\n" +" utolsó elÅ‘tti könyvtárat távolÃtja el.\n" +" \n" +" A „dirs†beépÃtett parancs listázza a könyvtárvermet. \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve érvénytelen argumentum vagy könyvtárváltás\n" +" során történÅ‘ hiba esetén." #: builtins.c:1818 msgid "" @@ -3860,25 +4893,41 @@ msgid "" " \twith its position in the stack\n" " \n" " Arguments:\n" -" +N\tDisplays the Nth entry counting from the left of the list shown " -"by\n" +" +N\tDisplays the Nth entry counting from the left of the list shown by\n" " \tdirs when invoked without options, starting with zero.\n" " \n" -" -N\tDisplays the Nth entry counting from the right of the list shown " -"by\n" +" -N\tDisplays the Nth entry counting from the right of the list shown by\n" " \tdirs when invoked without options, starting with zero.\n" " \n" " Exit Status:\n" " Returns success unless an invalid option is supplied or an error occurs." msgstr "" +"A könyvtárverem megjelenÃtése.\n" +" \n" +" MegjelenÃti a jelenleg megjegyzett könyvtárakat. A könyvtárakat a\n" +" „pushd†paranccsal lehet a verembe rakni; és a „popd†paranccsal kiven-\n" +" ni.\n" +" \n" +" Kapcsolók:\n" +" -c a könyvtárverem törlése az összes elem eltávolÃtásával\n" +" -l a saját könyvtárat ne rövidÃtse a listázáskor egy tilde (~)\n" +" -p a könyvtárverem kiÃrása soronként egy elemmel\n" +" -v a könyvtárverem kiÃrása soronként egy elemmel, a vermen\n" +" belüli pozÃció jelölésével\n" +" \n" +" Argumentumok:\n" +" +N N darab bejegyzést jelenÃt meg az argumentum nélkül a dirs\n" +" által megjelenÃtett listán balról számolva, nullától kezdve\n" +" -N N darab bejegyzést jelenÃt meg a listából jobbról számolva \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve érvénytelen argumentum vagy hiba esetén." #: builtins.c:1847 msgid "" "Set and unset shell options.\n" " \n" " Change the setting of each shell option OPTNAME. Without any option\n" -" arguments, list all shell options with an indication of whether or not " -"each\n" +" arguments, list all shell options with an indication of whether or not each\n" " is set.\n" " \n" " Options:\n" @@ -3892,6 +4941,22 @@ msgid "" " Returns success if OPTNAME is enabled; fails if an invalid option is\n" " given or OPTNAME is disabled." msgstr "" +"ParancsételmezÅ‘-kapcsolók beállÃtása és törlése.\n" +" \n" +" Minden megadott OPTNÉV kapcsoló beállÃtása. Argumentumok nélkül hÃvva\n" +" egy teljes lista kiÃrása a parancsértelmezÅ‘ kapcsolóiról, jelezve azok\n" +" állapotát.\n" +" \n" +" Kapcsolók:\n" +" -o OPTNEVek korlátozása a „set -oâ€-val használtakra\n" +" -p minden kapcsoló kilistázása állapottal\n" +" -q kimenet elnyelése\n" +" -s minden OPTNÉV engedélyezése\n" +" -u minden OPTNÉV tiltása\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, ha OPTNÉV engedélyezve van; sikertelenül, ha hi-\n" +" bás kapcsolókat kap vagy OPTNÉV tiltva van." #: builtins.c:1868 msgid "" @@ -3901,36 +4966,51 @@ msgid "" " -v var\tassign the output to shell variable VAR rather than\n" " \t\tdisplay it on the standard output\n" " \n" -" FORMAT is a character string which contains three types of objects: " -"plain\n" -" characters, which are simply copied to standard output; character " -"escape\n" +" FORMAT is a character string which contains three types of objects: plain\n" +" characters, which are simply copied to standard output; character escape\n" " sequences, which are converted and copied to the standard output; and\n" -" format specifications, each of which causes printing of the next " -"successive\n" +" format specifications, each of which causes printing of the next successive\n" " argument.\n" " \n" -" In addition to the standard format specifications described in printf" -"(1)\n" +" In addition to the standard format specifications described in printf(1)\n" " and printf(3), printf interprets:\n" " \n" " %b\texpand backslash escape sequences in the corresponding argument\n" " %q\tquote the argument in a way that can be reused as shell input\n" " \n" " Exit Status:\n" -" Returns success unless an invalid option is given or a write or " -"assignment\n" +" Returns success unless an invalid option is given or a write or assignment\n" " error occurs." msgstr "" +"FORMÃTUM alapján kiÃrja az ARGUMENTUMOKat.\n" +" \n" +" Kapcsolók:\n" +" -v változó kimenet VÃLTOZÓ nevű változóba Ãrása a szabványos\n" +" kimenet helyett\n" +" \n" +" FORMÃTUM egy karakterlánc, amely három tÃpusú primitÃvekbÅ‘l áll: egy-\n" +" szerű karakterek, amelyeket a parancs a kimenetre másol; escape-karak-\n" +" tersorozatok, amelyeket átalakÃtva másol a kimenetre; valamint formá-\n" +" tumjelzÅ‘k, amelyek rendre a következÅ‘ argumentum kiÃrását szabályoz-\n" +" zák.\n" +" \n" +" A printf(1) és printf(3) által használt szokásos jelzÅ‘kön túl a követ-\n" +" kezÅ‘ket ismeri a printf parancs:\n" +" \n" +" %b karakterlánc kiÃrása az escape-szekvenciák értelmezése után\n" +" %q argumentum idézÅ‘jelezése olyan módon, hogy parancsértelmezÅ‘\n" +" bemeneteként használható legyen\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve ha hibás kapcsolókat kap, vagy az Ãrás/ér-\n" +" tékadás hibával járt." #: builtins.c:1895 msgid "" "Specify how arguments are to be completed by Readline.\n" " \n" -" For each NAME, specify how arguments are to be completed. If no " -"options\n" -" are supplied, existing completion specifications are printed in a way " -"that\n" +" For each NAME, specify how arguments are to be completed. If no options\n" +" are supplied, existing completion specifications are printed in a way that\n" " allows them to be reused as input.\n" " \n" " Options:\n" @@ -3949,30 +5029,55 @@ msgid "" " Exit Status:\n" " Returns success unless an invalid option is supplied or an error occurs." msgstr "" +"Megadja, hogy a Readline hogyan egészÃtse ki az argumentumokat.\n" +" \n" +" Minden NÉV-hez megadja, hogyan egészÃtse ki a Readline az argumentumo-\n" +" kat. Ha nincsenek kapcsolók megadva, a jelenlegi érték kerül kiÃrásra,\n" +" újrafelhasználható módon.\n" +" \n" +" Kapcsolók:\n" +" -p meglévÅ‘ kiegészÃtésmegadások listázása újrahasználható módon\n" +" -r kiegészÃtésmegadások törlése minden NÉV-tÅ‘l; vagy ha nincs\n" +" NÉV megadva, az összes törlése\n" +" -D kiegészÃtések és műveletek alkalmazása alapértelmezésben, ha\n" +" az adott parancshoz nincs kiegészÃtés megadva\n" +" -E kiegészÃtések és műveletek alkalmazása az „üres†parancsok-\n" +" ra, vagyis a sor elején\n" +" \n" +" KiegészÃtéskor a műveletek a nagybetűs kapcsolók felsorolásának sor-\n" +" rendjében kÃsérli meg a Readline. A -D elsÅ‘bbséget élvez a -E-vel szem-\n" +" ben.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve érvénytelen kapcsoló és hiba esetén." #: builtins.c:1923 msgid "" "Display possible completions depending on the options.\n" " \n" " Intended to be used from within a shell function generating possible\n" -" completions. If the optional WORD argument is supplied, matches " -"against\n" +" completions. If the optional WORD argument is supplied, matches against\n" " WORD are generated.\n" " \n" " Exit Status:\n" " Returns success unless an invalid option is supplied or an error occurs." msgstr "" +"Lehetséges kiegészÃtések megjelenÃtése a kapcsolóktól függÅ‘en.\n" +" \n" +" Függvényben való használatra szolgál a lehetséges kiegészÃtések gene-\n" +" rálása céljából. Ha az elhagyható SZÓ argumentum is meg van adva, SZÓ-\n" +" ra elölrÅ‘l illeszkedÅ‘ találatok jelennek csak meg.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel lép ki, kivéve érvénytelen kapcsoló vagy hiba esetén." #: builtins.c:1938 msgid "" "Modify or display completion options.\n" " \n" -" Modify the completion options for each NAME, or, if no NAMEs are " -"supplied,\n" -" the completion currently begin executed. If no OPTIONs are givenm, " -"print\n" -" the completion options for each NAME or the current completion " -"specification.\n" +" Modify the completion options for each NAME, or, if no NAMEs are supplied,\n" +" the completion currently begin executed. If no OPTIONs are givenm, print\n" +" the completion options for each NAME or the current completion specification.\n" " \n" " Options:\n" " \t-o option\tSet completion option OPTION for each NAME\n" @@ -3993,29 +5098,49 @@ msgid "" " Returns success unless an invalid option is supplied or NAME does not\n" " have a completion specification defined." msgstr "" +"KiegészÃtési beállÃtások módosÃtása vagy kiÃrása.\n" +" \n" +" KiegészÃtési beállÃtások listázása minden NÉV-hez, vagy ha nincs NÉV\n" +" megadva, akkor az éppen zajló kiegészÃtésre. Ha nincs KAPCSOLÓ megad-\n" +" va, kiÃrja a kiegészÃtési beállÃtásokat minden NÉV-hez vagy az aktuá-\n" +" lis kiegészÃtéshez.\n" +" \n" +" Kapcsolók:\n" +" -o kapcsoló KAPCSOLÓ kiegészÃtései beállÃtás bekapcsolása minden\n" +" NÉV-hez\n" +" -D Az alapértelmezett kiegészÃtés beállÃtásainak módo-\n" +" sÃtása\n" +" -E Az üres kiegészÃtés beállÃtásainak módosÃtása\n" +" \n" +" „-o†helyett „+o†használatával a beállÃtás kikapcsolható.\n" +" \n" +" Argumentumok:\n" +" \n" +" Minden NÉV egy parancsra vonatkozik, amelyhez egy kiegészÃtést elÅ‘zÅ‘leg\n" +" meg kell adni a „complete†paranccsal. Ha nincs NÉV megadva, a compopt-\n" +" ot egy éppen kiegészÃtéseket generáló függvénybÅ‘l kell hÃvni, és a zaj-\n" +" ló generálásra fog vonatkozni.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel lép ki, kivéve ha érvénytelen kapcsolókat kap, vagy NÉV nincs\n" +" még megadva." #: builtins.c:1968 msgid "" "Read lines from the standard input into an indexed array variable.\n" " \n" -" Read lines from the standard input into the indexed array variable " -"ARRAY, or\n" -" from file descriptor FD if the -u option is supplied. The variable " -"MAPFILE\n" +" Read lines from the standard input into the indexed array variable ARRAY, or\n" +" from file descriptor FD if the -u option is supplied. The variable MAPFILE\n" " is the default ARRAY.\n" " \n" " Options:\n" -" -n count\tCopy at most COUNT lines. If COUNT is 0, all lines are " -"copied.\n" -" -O origin\tBegin assigning to ARRAY at index ORIGIN. The default " -"index is 0.\n" +" -n count\tCopy at most COUNT lines. If COUNT is 0, all lines are copied.\n" +" -O origin\tBegin assigning to ARRAY at index ORIGIN. The default index is 0.\n" " -s count \tDiscard the first COUNT lines read.\n" " -t\t\tRemove a trailing newline from each line read.\n" -" -u fd\t\tRead lines from file descriptor FD instead of the standard " -"input.\n" +" -u fd\t\tRead lines from file descriptor FD instead of the standard input.\n" " -C callback\tEvaluate CALLBACK each time QUANTUM lines are read.\n" -" -c quantum\tSpecify the number of lines read between each call to " -"CALLBACK.\n" +" -c quantum\tSpecify the number of lines read between each call to CALLBACK.\n" " \n" " Arguments:\n" " ARRAY\t\tArray variable name to use for file data.\n" @@ -4024,15 +5149,41 @@ msgid "" " CALLBACK is evaluated, it is supplied the index of the next array\n" " element to be assigned as an additional argument.\n" " \n" -" If not supplied with an explicit origin, mapfile will clear ARRAY " -"before\n" +" If not supplied with an explicit origin, mapfile will clear ARRAY before\n" " assigning to it.\n" " \n" " Exit Status:\n" -" Returns success unless an invalid option is given or ARRAY is readonly " -"or\n" +" Returns success unless an invalid option is given or ARRAY is readonly or\n" " not an indexed array." msgstr "" +"Sorok beolvasása a szabványos bemenetrÅ‘l egy indexelt tömbbe.\n" +" \n" +" Sorok beolvasása a szabványos bemenetrÅ‘l – vagy -u megadása esetén FD\n" +" fájlleÃróból – egy megadott nevű TÖMB-be (elhagyása esetén $ARRAY-be).\n" +" \n" +" Kapcsolók:\n" +" -n szám Legfeljebb SZÃM sor másolása. Ha szám 0, minden sor\n" +" másolásra kerül\n" +" -O kezdet KEZDET számú indextÅ‘l kezdje a TÖMB-be másolást.\n" +" Alapértelmezés: 0\n" +" -s szám Az elsÅ‘ SZÃM sor eldobása olvasáskor\n" +" -t A sorok végérÅ‘l a záró újsor eltávolÃtása\n" +" -u fd Szabványos bemenet helyett FD fájlleÃróból olvasson\n" +" -C parancs PARANCS végrehajtása minden TÃVOLSÃG sor után\n" +" -c távolság PARANCS végrehajtásai között beolvasott sorok száma\n" +" \n" +" Argumentumok:\n" +" TÖMB Beolvasáshoz használt tömb neve\n" +" \n" +" Ha -C -c nélkül van megadva, az alapértelmezett távolság 5000.\n" +" PARANCS végrehajtásakor utolsó argumentumként a parancs megkapja a kö-\n" +" vetkezÅ‘ beolvasandó elem indexét.\n" +" \n" +" Ha nincs KEZDET megadva, a mapfile törli a TÖMB tömböt olvasás elÅ‘tt.\n" +" \n" +" Kilépési kód:\n" +" Sikerrel tér vissza, kivéve érvénytelen kapcsoló vagy csak olvasható,\n" +" vagy nem indexelt TÖMB megadása esetén." #: builtins.c:2001 msgid "" @@ -4040,2263 +5191,6 @@ msgid "" " \n" " A synonym for `mapfile'." msgstr "" - -#~ msgid "xrealloc: cannot reallocate %lu bytes (%lu bytes allocated)" -#~ msgstr "xmalloc: nem lehet lefoglalni %lu bájtot (%lu bájt lefoglalva)" - -#, fuzzy -#~ msgid "xrealloc: cannot allocate %lu bytes" -#~ msgstr "xmalloc: nem lehet lefoglalni %lu bájtot (%lu bájt lefoglalva)" - -#, fuzzy -#~ msgid "xrealloc: %s:%d: cannot reallocate %lu bytes (%lu bytes allocated)" -#~ msgstr "xmalloc: nem lehet lefoglalni %lu bájtot (%lu bájt lefoglalva)" - -#~ msgid "Display the list of currently remembered directories. Directories" -#~ msgstr "Megjeleníti a jelenleg feljegyzett könyvtárakat. Könyvtárak" - -#~ msgid "find their way onto the list with the `pushd' command; you can get" -#~ msgstr "listába való behelyezés a 'pushd' parancs végzi és az eltávolítást" - -#~ msgid "back up through the list with the `popd' command." -#~ msgstr "pedig a 'popd' utasítással lehet végrehajtani." - -#~ msgid "" -#~ "The -l flag specifies that `dirs' should not print shorthand versions" -#~ msgstr "A '-l' opció hatására a 'dirs' nem használja a gyorsírás módot" - -#~ msgid "" -#~ "of directories which are relative to your home directory. This means" -#~ msgstr "a saját könyvtárból nyíló mappákra. Ez azt jelenti, hogy" - -#~ msgid "that `~/bin' might be displayed as `/homes/bfox/bin'. The -v flag" -#~ msgstr "" -#~ "a '~/bin' helyet '/home/bfox/bin' fog megjelenni. A '-v' azt hatására" - -#~ msgid "causes `dirs' to print the directory stack with one entry per line," -#~ msgstr "a 'dirs' a verem elemeit külön sorba írja ki" - -#~ msgid "" -#~ "prepending the directory name with its position in the stack. The -p" -#~ msgstr "és a könyvtár neve elé írja a veremben elfoglalt helyét is. A '-p'" - -#~ msgid "flag does the same thing, but the stack position is not prepended." -#~ msgstr "ugyanezt csinálja csak nem írja ki az elfoglalt helyet." - -#~ msgid "" -#~ "The -c flag clears the directory stack by deleting all of the elements." -#~ msgstr "A '-c'-vel az egész könyvtár vermet kitörli." - -#, fuzzy -#~ msgid "" -#~ "+N displays the Nth entry counting from the left of the list shown by" -#~ msgstr "+N\tMegmutatja a verem N-edik bejegyzését a számolást balról kezdi" - -#, fuzzy -#~ msgid " dirs when invoked without options, starting with zero." -#~ msgstr "\taz elsõ eleme a nulladik." - -#, fuzzy -#~ msgid "" -#~ "-N displays the Nth entry counting from the right of the list shown by" -#~ msgstr "-N\tMegmutatja a verem N-edik bejegyzését a számolást jobbról kezdi" - -#~ msgid "Adds a directory to the top of the directory stack, or rotates" -#~ msgstr "Egy új könyvtárat ad a könyvtár veremhez, vagy eltolja" - -#~ msgid "the stack, making the new top of the stack the current working" -#~ msgstr "a vermet, létrehozza a verem elejét az aktuális munka " - -#~ msgid "directory. With no arguments, exchanges the top two directories." -#~ msgstr "könyvtárban. Opciók nélkül felcseréli az elsõ két könyvtárat." - -#, fuzzy -#~ msgid "+N Rotates the stack so that the Nth directory (counting" -#~ msgstr "+N\t\tEltolja a verem N-edik könyvtárát (a számolást" - -#, fuzzy -#~ msgid " from the left of the list shown by `dirs', starting with" -#~ msgstr "\t\tbalról kezdi a 'dirs' listájában) legelõre." - -#, fuzzy -#~ msgid " zero) is at the top." -#~ msgstr "\tjobbról kezdi) legelõre." - -#, fuzzy -#~ msgid "-N Rotates the stack so that the Nth directory (counting" -#~ msgstr "-N\t\tEltolja a verem N-edik könyvtárát (a számolást" - -#, fuzzy -#~ msgid " from the right of the list shown by `dirs', starting with" -#~ msgstr "\t\tbalról kezdi a 'dirs' listájában) legelõre." - -#, fuzzy -#~ msgid "-n suppress the normal change of directory when adding directories" -#~ msgstr "" -#~ "-n\tletiltja a normális könyvtár váltást. Amikor a könyvtárakat váltunk" - -#, fuzzy -#~ msgid " to the stack, so only the stack is manipulated." -#~ msgstr "\takkor a veremhez adjuk az aktuális könyvtárat." - -#, fuzzy -#~ msgid "dir adds DIR to the directory stack at the top, making it the" -#~ msgstr "dir\tA DIR-t behelyezi a verem elejére és az" - -#, fuzzy -#~ msgid " new current working directory." -#~ msgstr "\tlesz az aktuális munkakönyvtár." - -#~ msgid "You can see the directory stack with the `dirs' command." -#~ msgstr "A könyvtár vermet a 'dirs' paranccsal tekintheti meg." - -#~ msgid "Removes entries from the directory stack. With no arguments," -#~ msgstr "Bejegyzést távolít el a könyvtár verembõl. Opciók nélkül" - -#~ msgid "removes the top directory from the stack, and cd's to the new" -#~ msgstr "eltávolítja a verem elsõ bejegyzését, és átlép a soron következõ" - -#~ msgid "top directory." -#~ msgstr "könyvtárba." - -#, fuzzy -#~ msgid "+N removes the Nth entry counting from the left of the list" -#~ msgstr "+N\tEltávolítja a verem N-edik bejegyzését (a számolást" - -#, fuzzy -#~ msgid " shown by `dirs', starting with zero. For example: `popd +0'" -#~ msgstr "\tbalról kezdi és nullával indítja) .Például a 'popd +0'" - -#, fuzzy -#~ msgid " removes the first directory, `popd +1' the second." -#~ msgstr "\taz elsõ könyvtárat távolítja el, a 'pop +1' a másodikat." - -#, fuzzy -#~ msgid "-N removes the Nth entry counting from the right of the list" -#~ msgstr "+N\tEltávolítja a verem N-edik bejegyzését (a számolást" - -#, fuzzy -#~ msgid " shown by `dirs', starting with zero. For example: `popd -0'" -#~ msgstr "\tbalról kezdi és nullával indítja) .Például a 'popd -0'" - -#, fuzzy -#~ msgid " removes the last directory, `popd -1' the next to last." -#~ msgstr "\taz utolsó könyvtárat távolítja el, a 'pop +1' a .utolsóelõttit" - -#, fuzzy -#~ msgid "" -#~ "-n suppress the normal change of directory when removing directories" -#~ msgstr "" -#~ "-n\tletiltja a normális könyvtár váltást. Amikor a könyvtárakat váltunk" - -#, fuzzy -#~ msgid " from the stack, so only the stack is manipulated." -#~ msgstr "\takkor a verembõl eltávolítja az aktuális könyvtárat." - -#, fuzzy -#~ msgid "" -#~ "Exit from within a FOR, WHILE or UNTIL loop. If N is specified,\n" -#~ " break N levels." -#~ msgstr "Kilép egy FOR, WHILE vagy UNTIL hurokból. Ha N meg van határozva," - -#~ msgid "Obsolete. See `declare'." -#~ msgstr "Elavult. Lásd 'declare'" - -#~ msgid "" -#~ "Output the ARGs. If -n is specified, the trailing newline is suppressed." -#~ msgstr "" -#~ "Kimenet az ARG. Ha -n használja, akkor a sorvégi újsor jelet nem veszi " -#~ "figyelembe." - -#~ msgid "" -#~ "Read ARGs as input to the shell and execute the resulting command(s)." -#~ msgstr "" -#~ "Beolvassa az ARG tartalmát a bemenetrõl a parancsértelmezõhõz és " -#~ "elindítja." - -#~ msgid "Logout of a login shell." -#~ msgstr "Kilépés a parancsértelmezõbõl." - -#, fuzzy -#~ msgid "" -#~ "Causes a function to exit with the return value specified by N. If N\n" -#~ " is omitted, the return status is that of the last command." -#~ msgstr "" -#~ "A funkció hatására kilép és a visszatérési értéke az N-ben meghatározott " -#~ "érték lesz." - -#, fuzzy -#~ msgid "" -#~ "The positional parameters from $N+1 ... are renamed to $1 ... If N is\n" -#~ " not given, it is assumed to be 1." -#~ msgstr "A pozicionáló paramétereket átnevezi $N+1-rõl $1-re.Ha az N" - -#, fuzzy -#~ msgid "" -#~ "Print the accumulated user and system times for processes run from\n" -#~ " the shell." -#~ msgstr "Kiírja felhasználó és a rendszer mennyi idõ használt fel arra, hogy" - -#~ msgid "Missing `}'" -#~ msgstr "Hiányzó '}'" - -#~ msgid "brace_expand> " -#~ msgstr "brace_expand> " - -#~ msgid "Attempt to free unknown command type `%d'.\n" -#~ msgstr "Megpróbálom felszabadítani az ismeretlen `%d' parancs típust.\n" - -#~ msgid "Report this to %s\n" -#~ msgstr "Kérem jelentse ezt a %s -re \n" - -#~ msgid "Stopping myself..." -#~ msgstr "Leállítom magam..." - -#~ msgid "Tell %s to fix this someday.\n" -#~ msgstr "Mondja el %s -nek, hogy kijavíthassa\n" - -#~ msgid "execute_command: bad command type `%d'" -#~ msgstr "execute_command: rossz parancs típus `%d'" - -#~ msgid "real\t" -#~ msgstr "valós\t" - -#~ msgid "user\t" -#~ msgstr "felhasználói\t" - -#~ msgid "sys\t" -#~ msgstr "rendszer\t" - -#~ msgid "" -#~ "real\t0m0.00s\n" -#~ "user\t0m0.00s\n" -#~ "sys\t0m0.00s\n" -#~ msgstr "" -#~ "valós\t0m0.00s\n" -#~ "felhasználói\t0m0.00s\n" -#~ "rendszer\t0m0.00s\n" - -#~ msgid "cannot duplicate fd %d to fd 1: %s" -#~ msgstr "nem másolható a fd %d fd 1: %s-re" - -#~ msgid "%s: output redirection restricted" -#~ msgstr "%s kimenet átirányítás fenntartva" - -#~ msgid "Out of memory!" -#~ msgstr "Elfogyott a memória!" - -#~ msgid "You have already added item `%s'\n" -#~ msgstr "A `%s' elem egyszer már hozzá lett adva\n" - -#~ msgid "You have entered %d (%d) items. The distribution is:\n" -#~ msgstr "Ön %d (%d) elemet írt be. A terjesztés:\n" - -#~ msgid "%s: bg background job?" -#~ msgstr "%s: bg háttér munkafolyamat?" - -#~ msgid "" -#~ "Redirection instruction from yyparse () '%d' is\n" -#~ "out of range in make_redirection ()." -#~ msgstr "" -#~ "Az átirányítási utasítás a yyparse-ból () '%d' \n" -#~ "túl van a határon a make_redirection ().-ben" - -#~ msgid "clean_simple_command () got a command with type %d." -#~ msgstr "clean_simple_command () kaptam egy %d típusú parancsot." - -#~ msgid "got errno %d while waiting for %d" -#~ msgstr "kaptam egy %d hibát amíg vártam a %d-re" - -#~ msgid "syntax error near unexpected token `%c'" -#~ msgstr "szintaktikai hiba a váratlan %c vezérjel körül" - -#~ msgid "print_command: bad command type `%d'" -#~ msgstr "print_command: rossz parancs típus `%d'" - -#~ msgid "cprintf: bad `%%' argument (%c)" -#~ msgstr "cprintf: rossz `%%' paraméter (%c)" - -#~ msgid "option `%s' requires an argument" -#~ msgstr "opciók a `%s' paramétert igényel" - -#~ msgid "%s: unrecognized option" -#~ msgstr "a %s ismeretlen opció" - -#~ msgid "`-c' requires an argument" -#~ msgstr "`-c' paramétert igényel" - -#~ msgid "%s: cannot execute directories" -#~ msgstr "%s: könyvtárakat nem lehet futtatni" - -#~ msgid "Bad code in sig.c: sigprocmask" -#~ msgstr "Hibás kód a sig.c-ben: sigprocmask" - -#~ msgid "bad substitution: no ending `}' in %s" -#~ msgstr "rossz behelyettesítés: nincs lezáró } a %s-ben" - -#~ msgid "%s: bad array subscript" -#~ msgstr "%s rossz tömb a tömbindexben" - -#~ msgid "can't make pipes for process substitution: %s" -#~ msgstr "nem lehet létrehozni a pipe-ot feladat behelyettesítéshez: %s" - -#~ msgid "reading" -#~ msgstr "olvasás" - -#~ msgid "process substitution" -#~ msgstr "feladat behelyettesítése" - -#~ msgid "command substitution" -#~ msgstr "parancs behelyettesítése" - -#~ msgid "Can't reopen pipe to command substitution (fd %d): %s" -#~ msgstr "" -#~ "Nem lehet újra megnyitni a \"pipe\"-ot a parancs behelyettesítéshez(fd %" -#~ "d): %s" - -#~ msgid "$%c: unbound variable" -#~ msgstr "$%c felszabadított változó" - -#~ msgid "%s: bad arithmetic substitution" -#~ msgstr "%s: rossz számtani helyettesítés" - -#~ msgid "-%s: binary operator expected" -#~ msgstr "-%s:bináris mûvelet szükséges" - -#~ msgid "%s[%s: bad subscript" -#~ msgstr "%s[%s: rossz tömbindex" - -#~ msgid "[%s: bad subscript" -#~ msgstr "[%s: rossz tömbindex" - -#~ msgid "digits occur in two different argv-elements.\n" -#~ msgstr "számjegyek fordultak elõ két különbözõ \"argv\"elemben.\n" - -#~ msgid "option %c\n" -#~ msgstr "opció: %c\n" - -#~ msgid "option a\n" -#~ msgstr "opció a\n" - -#~ msgid "option b\n" -#~ msgstr "opció b\n" - -#~ msgid "option c with value `%s'\n" -#~ msgstr "opció c %s értékkel\n" - -#~ msgid "?? sh_getopt returned character code 0%o ??\n" -#~ msgstr "?? sh_getopt visszakapott karakter kód 0%o ??\n" - -#~ msgid "non-option ARGV-elements: " -#~ msgstr "nem opció az \"argv\"elemek" - -#~ msgid "%s: Unknown flag %s.\n" -#~ msgstr "%s: Ismeretlen jel %s.\n" - -#~ msgid "Unknown directive `%s'" -#~ msgstr "Ismeretlen direktíva %s" - -#~ msgid "%s requires an argument" -#~ msgstr "a %s paramétert igényel" - -#~ msgid "%s must be inside of a $BUILTIN block" -#~ msgstr "%s-nek a $BUILTIN blokkon belül kell hogy legyen" - -#~ msgid "%s found before $END" -#~ msgstr "%s találtam az $END elõtt" - -#~ msgid "%s already has a function (%s)" -#~ msgstr "%s már létezõ függvény (%s)" - -#~ msgid "%s already had a docname (%s)" -#~ msgstr "a %s már kész dokumentum (%s)" - -#~ msgid "%s already has short documentation (%s)" -#~ msgstr "a %s már létezõ rövid dokumentáció (%s)" - -#~ msgid "%s already has a %s definition" -#~ msgstr "a %s már létezõ %s meghatározás(definition)" - -#~ msgid "mkbuiltins: Out of virtual memory!\n" -#~ msgstr "mkbuiltins: A virtuális memória elfogyott!\n" - -#~ msgid "read [-r] [-p prompt] [-a array] [-e] [name ...]" -#~ msgstr "read [-r] [-p kérdés] [-a tömb] [-e] [név ...]" - -#~ msgid "%[DIGITS | WORD] [&]" -#~ msgstr "%[SZÁMOK | SZÓ] [&]" - -#~ msgid "variables - Some variable names and meanings" -#~ msgstr "változók - Néhány változó neve és jelentése" - -#~ msgid "`alias' with no arguments or with the -p option prints the list" -#~ msgstr "" -#~ "`alias' paraméterek nélkül vagy -p opcióval kiírja az aliasok listáját" - -#~ msgid "of aliases in the form alias NAME=VALUE on standard output." -#~ msgstr "az NÉV=ÉRTÉK formában a standard kimeneten." - -#~ msgid "Otherwise, an alias is defined for each NAME whose VALUE is given." -#~ msgstr "" -#~ "Egyébként, az alias-ban meghatározott mindegyik NÉV-nek az ÉRTÉK lesz " -#~ "átadva." - -#~ msgid "A trailing space in VALUE causes the next word to be checked for" -#~ msgstr " " - -#~ msgid "alias substitution when the alias is expanded. Alias returns" -#~ msgstr "Az alias visszatérési értéke " - -#~ msgid "true unless a NAME is given for which no alias has been defined." -#~ msgstr "igaz hacsak a NÉV alatt nincs alias meghatározva." - -#~ msgid "then remove all alias definitions." -#~ msgstr "eltávolítja az összes alias meghatározást." - -#~ msgid "Bind a key sequence to a Readline function, or to a macro. The" -#~ msgstr "" -#~ "a bind egy kulcs szekvencia a readline funkcióhoz vagy egy makróhoz." - -#~ msgid "syntax is equivalent to that found in ~/.inputrc, but must be" -#~ msgstr "A szintaktika megegyezik az ~/.inputrc-vel, de át kell" - -#~ msgid "" -#~ "passed as a single argument: bind '\"\\C-x\\C-r\": re-read-init-file'." -#~ msgstr "" -#~ "adni egy egyszerû paraméterrel: bind '\"\\C-x\\C-r\": re-read-init-file'." - -#~ msgid "Arguments we accept:" -#~ msgstr "A paraméterek amiket elfogadunk:" - -#~ msgid "" -#~ " -m keymap Use `keymap' as the keymap for the duration of this" -#~ msgstr " -m keymap A `keymap'-ot használja keymap-ként az amíg" - -#~ msgid " command. Acceptable keymap names are emacs," -#~ msgstr "" -#~ " a parancs fut. Elfogadható keymap névnek emacs-ok," - -#~ msgid "" -#~ " emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move," -#~ msgstr "" -#~ " emacs-standard, emacs-meta, emacs-ctlx, vi, vi-move," - -#~ msgid " vi-command, and vi-insert." -#~ msgstr " vi-parancs, és vi-beszúrás." - -#~ msgid " -l List names of functions." -#~ msgstr " -l A funkciók neveinek a listája." - -#~ msgid " -P List function names and bindings." -#~ msgstr " -P A nevek és a hozzájuk tartozó bind-ok listája." - -#~ msgid "" -#~ " -p List functions and bindings in a form that can be" -#~ msgstr "" -#~ " -p A funkciók és bind-ok listája bemenetként újra " -#~ "felszanálható" - -#~ msgid " reused as input." -#~ msgstr " formában." - -#~ msgid " -r keyseq Remove the binding for KEYSEQ." -#~ msgstr " -r keyseq Bind eltávolítása a KEYSEQ-rõl." - -#~ msgid " -f filename Read key bindings from FILENAME." -#~ msgstr " -f FÁJLNÉN Bind-ok olvasása a FÁJLNÉV fájlból." - -#~ msgid "" -#~ " -q function-name Query about which keys invoke the named function." -#~ msgstr "" -#~ " -q function-name Lekérdezi, hogy milyen billentyût hív meg a funkció." - -#~ msgid " -V List variable names and values" -#~ msgstr " -V A változók neveinek és értékeinek a listája." - -#~ msgid "" -#~ " -v List variable names and values in a form that can" -#~ msgstr "" -#~ " -v A változók és értékeik neveinek a listája " -#~ "bemenetként " - -#~ msgid " be reused as input." -#~ msgstr " újra felhasználható formában." - -#~ msgid "" -#~ " -S List key sequences that invoke macros and their " -#~ "values" -#~ msgstr "" -#~ " -S A kulcs szekvenciák listája a meghívott makrókkal és " -#~ "azok értékével" - -#~ msgid "" -#~ " -s List key sequences that invoke macros and their " -#~ "values in" -#~ msgstr "" -#~ " -S A kulcs szekvenciák listája a meghívott makrókkal és " -#~ "azok értékével" - -#~ msgid " a form that can be reused as input." -#~ msgstr " melyeket fel lehet használni bemenetként." - -#~ msgid "break N levels." -#~ msgstr "akkor N szintet lép ki." - -#~ msgid "If N is specified, resume at the N-th enclosing loop." -#~ msgstr "" -#~ "Ha N meg van határozva akkor N szinttel magasabb hurokba lép vissza." - -#~ msgid "Run a shell builtin. This is useful when you wish to rename a" -#~ msgstr "" -#~ "Egy beépített utasítást futtat , Ez akkor hasznos ha átakar nevezni egy" - -#~ msgid "shell builtin to be a function, but need the functionality of the" -#~ msgstr "" -#~ "egy beépített parancsot funkcióvá, de szüksége van a beépített parancs" - -#~ msgid "builtin within the function itself." -#~ msgstr "eredeti funkciójára." - -#~ msgid "Change the current directory to DIR. The variable $HOME is the" -#~ msgstr "Belép a dir könyvtárba.A $HOME változó az alapértelmezett" - -#~ msgid "default DIR. The variable $CDPATH defines the search path for" -#~ msgstr "DIR. A $CDPATH változó meghatározza a keresési útvonalát a DIR-t" - -#~ msgid "the directory containing DIR. Alternative directory names in CDPATH" -#~ msgstr "tartalmazó könyvtárnak. Az alternatív könyvtár neveket a CDPATH-ban" - -#~ msgid "are separated by a colon (:). A null directory name is the same as" -#~ msgstr "kettõsponttal(:) kell elválasztani. Az üres könyvtár név azonos" - -#~ msgid "the current directory, i.e. `.'. If DIR begins with a slash (/)," -#~ msgstr "a jelenlegi könyvtárral `.'. Ha a DIR per jellel kezdõdik (/)," - -#~ msgid "then $CDPATH is not used. If the directory is not found, and the" -#~ msgstr "akkor a $CDPATH nem használható. Ha a könyvtár nem található és a" - -#~ msgid "shell option `cdable_vars' is set, then try the word as a variable" -#~ msgstr "" -#~ "a parancsértelmezõ 'cdable_vars' nincs beállítva, akkor a szót " -#~ "megpróbálja mint " - -#~ msgid "name. If that variable has a value, then cd to the value of that" -#~ msgstr "" -#~ "változó névként. Ha ennek a változónak van értéke, akkor behelyettesíti a " -#~ "változó " - -#~ msgid "" -#~ "variable. The -P option says to use the physical directory structure" -#~ msgstr "" -#~ "értékét. A -P opció azt jelenti, hogy a fizikai könyvtár felépítést " -#~ "használja" - -#~ msgid "" -#~ "instead of following symbolic links; the -L option forces symbolic links" -#~ msgstr "" -#~ "a szimbolikus linkek helyet, a -L opció pedig a szimbolikus linkek " -#~ "követését" - -#~ msgid "to be followed." -#~ msgstr "erõlteti ki" - -#~ msgid "Print the current working directory. With the -P option, pwd prints" -#~ msgstr "Kiírja a könyvtárat ahol áll. A -P opcióban a pwd a " - -#~ msgid "the physical directory, without any symbolic links; the -L option" -#~ msgstr "" -#~ "fizikai könyvtár felépítést mutatja, a szimbolikus linkek nélkül, a -L " -#~ "opció" - -#~ msgid "makes pwd follow symbolic links." -#~ msgstr "pedig követi a szimbolikus linkeket." - -#~ msgid "" -#~ "Runs COMMAND with ARGS ignoring shell functions. If you have a shell" -#~ msgstr "" -#~ "Egy PARANCS-ot futtat ARG-okkal figyelmen kívül hagyja a parancsértelmezõ" - -#~ msgid "function called `ls', and you wish to call the command `ls', you can" -#~ msgstr "" -#~ "funkcióit.Ha van egy funkció aminek a neve 'ls', és megakarja hívni, csak " -#~ "azt" - -#~ msgid "" -#~ "say \"command ls\". If the -p option is given, a default value is used" -#~ msgstr "" -#~ "kell mondani, hogy \"command ls\".Ha a -p opciót használja, akkor " -#~ "felhasználja" - -#~ msgid "" -#~ "for PATH that is guaranteed to find all of the standard utilities. If" -#~ msgstr "a PATH változót így biztos, hogy meg találja az alap programokat." - -#~ msgid "" -#~ "the -V or -v option is given, a string is printed describing COMMAND." -#~ msgstr "Ha a -V vagy a -v opciót használja akkor kiírja a COMMAND leírását." - -#~ msgid "The -V option produces a more verbose description." -#~ msgstr "A -V opció több információt ad vissza." - -#~ msgid "Declare variables and/or give them attributes. If no NAMEs are" -#~ msgstr "Változókat hoz létre és/vagy attribútumokat ad nekik. Ha nincs NÉV" - -#~ msgid "given, then display the values of variables instead. The -p option" -#~ msgstr "átadva, akkor a megjeleníti a változók értékeit. A -p opció" - -#~ msgid "will display the attributes and values of each NAME." -#~ msgstr "megjeleníti az attribútumokat és az mindegyik NÉV értékét." - -#~ msgid "The flags are:" -#~ msgstr "A lehetséges opciók:" - -#~ msgid " -a\tto make NAMEs arrays (if supported)" -#~ msgstr " -a\tlétrehozza a NÉV tömböt (ha támogatott)" - -#~ msgid " -f\tto select from among function names only" -#~ msgstr "7 -f\tcsak a funkciókat válassza ki" - -#~ msgid " -F\tto display function names without definitions" -#~ msgstr " -F\tmegjeleníti a funkció neveket a meghatározásuk nélkül" - -#~ msgid " -r\tto make NAMEs readonly" -#~ msgstr " -r\ta NEVEKET csak olvashatóvá teszi" - -#~ msgid " -x\tto make NAMEs export" -#~ msgstr " -x\ta NEVEKET exportálható módon írja ki" - -#~ msgid " -i\tto make NAMEs have the `integer' attribute set" -#~ msgstr " -i\tkiírja a NEVEKET amiknek 'szám' az attribútumuk" - -#~ msgid "Variables with the integer attribute have arithmetic evaluation (see" -#~ msgstr "A változók melyeknek szám az attribútumuk egy számot (lásd a " - -#~ msgid "`let') done when the variable is assigned to." -#~ msgstr "let) kell, hozzárendelni." - -#~ msgid "When displaying values of variables, -f displays a function's name" -#~ msgstr "Amikor megjeleníti a változók értékét a -f a funkciók nevét" - -#~ msgid "and definition. The -F option restricts the display to function" -#~ msgstr "és meghatározásukat mutatja. A -F opció szigorúan csak " - -#~ msgid "name only." -#~ msgstr "a funkciók nevét jeleníti meg." - -#~ msgid "" -#~ "Using `+' instead of `-' turns off the given attribute instead. When" -#~ msgstr "A '+' használata a '-' helyet az attribútumot törli.Amikor " - -#~ msgid "used in a function, makes NAMEs local, as with the `local' command." -#~ msgstr "" -#~ "egy funkcióban használja a létrehozott NÉV helyi lesz, mint a local " -#~ "parancsnál." - -#~ msgid "Create a local variable called NAME, and give it VALUE. LOCAL" -#~ msgstr "" -#~ "Létrehoz egy helyi változott melyet NÉV-nek hív és átadja neki az ÉRTÉKET." - -#~ msgid "have a visible scope restricted to that function and its children." -#~ msgstr "csak a funkción és gyermekein belül érvényes." - -#~ msgid "Output the ARGs. If -n is specified, the trailing newline is" -#~ msgstr "" -#~ "Kimenet az ARG. Ha -n használja, akkor a sorvégi újsort jelet nem veszi" - -#~ msgid "suppressed. If the -e option is given, interpretation of the" -#~ msgstr "" -#~ "figyelembe. Ha a -e opciót használja akkor a következõ 'backslash-escaped'" - -#~ msgid "following backslash-escaped characters is turned on:" -#~ msgstr "karaktereket használja:" - -#~ msgid "\t\\a\talert (bell)" -#~ msgstr "\t\\a\tébresztõ (csengõ)" - -#~ msgid "\t\\b\tbackspace" -#~ msgstr "\t\\b\ttörlés" - -#~ msgid "\t\\c\tsuppress trailing newline" -#~ msgstr "\t\\c\tsorvégi újsor kihagyása" - -#~ msgid "\t\\E\tescape character" -#~ msgstr "\t\\E\tescape karakter" - -#~ msgid "\t\\f\tform feed" -#~ msgstr "\t\\f\tlap dobás" - -#~ msgid "\t\\n\tnew line" -#~ msgstr "\t\\n\túj sor" - -#~ msgid "\t\\r\tcarriage return" -#~ msgstr "\t\\r\tkocsi vissza" - -#~ msgid "\t\\t\thorizontal tab" -#~ msgstr "\t\\t\tvízszintes tab" - -#~ msgid "\t\\v\tvertical tab" -#~ msgstr "\t\\v\tfüggõleges tab" - -#~ msgid "\t\\\\\tbackslash" -#~ msgstr "\t\\\\\tbackslash" - -#~ msgid "\t\\num\tthe character whose ASCII code is NUM (octal)." -#~ msgstr "\t\\num\ta NUM a karakter ASCII kódja (nyolcas számrendszerben)" - -#~ msgid "" -#~ "You can explicitly turn off the interpretation of the above characters" -#~ msgstr "A -E opcióval ki lehet kapcsolni a karakterek fenti" - -#~ msgid "with the -E option." -#~ msgstr "értelmezését." - -#~ msgid "Enable and disable builtin shell commands. This allows" -#~ msgstr "" -#~ "Engedélyezés és Letiltja parancsértelmezõ beépített utasításait. Ez azt " -#~ "jelenti," - -#~ msgid "you to use a disk command which has the same name as a shell" -#~ msgstr "" -#~ "hogy lehet használni a lemezen található parancsokat melyeknek a neve meg-" - -#~ msgid "builtin. If -n is used, the NAMEs become disabled; otherwise" -#~ msgstr "" -#~ "egyezik a beépítettével. Ha a -n használja, akkor a NÉV le lesz tiltva " -#~ "egyébként" - -#~ msgid "NAMEs are enabled. For example, to use the `test' found on your" -#~ msgstr "" -#~ "a NEVET engedélyezi. Például ha használni akarja a 'test' -et ami lemezen " -#~ "létezik" - -#~ msgid "path instead of the shell builtin version, type `enable -n test'." -#~ msgstr "a parancsértelelmezõ helyet, gépelje be 'enable -n test'." - -#~ msgid "On systems supporting dynamic loading, the -f option may be used" -#~ msgstr "" -#~ "Azokon a rendszereken ami támogatja a dinamikus betöltést, a -f opcióval" - -#~ msgid "to load new builtins from the shared object FILENAME. The -d" -#~ msgstr "be lehet tölteni a FÁJLNÉV fájlt és beépítettként használni. A -d" - -#~ msgid "option will delete a builtin previously loaded with -f. If no" -#~ msgstr "opció pedig letörli az elõzõleg a -f el betöltött programot. Ha nem" - -#~ msgid "non-option names are given, or the -p option is supplied, a list" -#~ msgstr "" -#~ "ír be semmit vagy a -p opciót használja akkor megjeleníti az engedélyéket " - -#~ msgid "of builtins is printed. The -a option means to print every builtin" -#~ msgstr "beépített parancsokat. A -a opció megjeleníti az összes beépített" - -#~ msgid "with an indication of whether or not it is enabled. The -s option" -#~ msgstr "parancsot akár engedélyezve akár nem. A -s opció kierõlteti" - -#~ msgid "restricts the output to the Posix.2 `special' builtins. The -n" -#~ msgstr "a Posix.2 speciális beépített parancsait.A -n kiírja az összes" - -#~ msgid "option displays a list of all disabled builtins." -#~ msgstr "letiltott beépített parancsot." - -#~ msgid "Getopts is used by shell procedures to parse positional parameters." -#~ msgstr "" -#~ "A getopts -ot a parancsértelmezõ használja a parancsok paraméterinek az " -#~ "elemzésére." - -#~ msgid "OPTSTRING contains the option letters to be recognized; if a letter" -#~ msgstr "Az OPTSTRING tartalmazza a felismerendõ opciókat, ha az opciókat" - -#~ msgid "is followed by a colon, the option is expected to have an argument," -#~ msgstr "kettõspont követi akkor valószínûleg paramétere is van," - -#~ msgid "which should be separated from it by white space." -#~ msgstr "amit javasolt elválasztani szóközzel." - -#~ msgid "Each time it is invoked, getopts will place the next option in the" -#~ msgstr "Minden hívásnál a getopts elhelyezi a $name" - -#~ msgid "shell variable $name, initializing name if it does not exist, and" -#~ msgstr "változóba, ha nem létezik a név akkor" - -#~ msgid "the index of the next argument to be processed into the shell" -#~ msgstr "a következõ paraméter az OPTIND változóba kerül." - -#~ msgid "variable OPTIND. OPTIND is initialized to 1 each time the shell or" -#~ msgstr "" -#~ "Az OPTIND változót csak egyszer kell létrehozni a parancsértelmezõben " -#~ "vagy " - -#~ msgid "a shell script is invoked. When an option requires an argument," -#~ msgstr "" -#~ "egy parancsállomány meghívásakor. Amikor az opció paramétert igényel" - -#~ msgid "getopts places that argument into the shell variable OPTARG." -#~ msgstr "akkor a getopts belehelyezi a paramétert az OPTARG változóba." - -#~ msgid "getopts reports errors in one of two ways. If the first character" -#~ msgstr "A getopts két módon képes képes a hibákat jelenteni.Ha az OPSTRING" - -#~ msgid "of OPTSTRING is a colon, getopts uses silent error reporting. In" -#~ msgstr "" -#~ "elsõ karakterre egy kettõspont, akkor a getopts csendes hibamódban van." - -#~ msgid "this mode, no error messages are printed. If an illegal option is" -#~ msgstr "" -#~ "Ebben az üzemmódjában nem ír hiba üzenetet. Ha érvénytelen opció talál" - -#~ msgid "seen, getopts places the option character found into OPTARG. If a" -#~ msgstr "abban az esetben a getopts az OPTARG változóba helyezi az opciót." - -#~ msgid "required argument is not found, getopts places a ':' into NAME and" -#~ msgstr "" -#~ "Ha egy szükséges paramétert nincs meg a getopts egy ':' helyez a NÉV-be és" - -#~ msgid "sets OPTARG to the option character found. If getopts is not in" -#~ msgstr "beállítja az OPTARG-ot a hibás opciókhoz. Ha a getopts nem csöndes" - -#~ msgid "silent mode, and an illegal option is seen, getopts places '?' into" -#~ msgstr "üzemmódban van és érvénytelen opciót talál, akkor a getopts egy '?'" - -#~ msgid "NAME and unsets OPTARG. If a required option is not found, a '?'" -#~ msgstr "" -#~ "a NÉV változóba és leállítja az OPTARG változót. Ha a szükséges opció " -#~ "nincs meg" - -#~ msgid "is placed in NAME, OPTARG is unset, and a diagnostic message is" -#~ msgstr "" -#~ "akkor egy '?' -et rak a NÉV-be és az OPTARG-ot leállítja és egy üzenetet " -#~ "küld" - -#~ msgid "If the shell variable OPTERR has the value 0, getopts disables the" -#~ msgstr "Ha az OPTERR változó értéke 0, akkor a getopts letiltja" - -#~ msgid "printing of error messages, even if the first character of" -#~ msgstr "az üzenetek küldését, még akkor is ha az OPTSTRING elsõ karakterre" - -#~ msgid "OPTSTRING is not a colon. OPTERR has the value 1 by default." -#~ msgstr "egy kettõspont. Az OPTERR alapértéke 1." - -#~ msgid "Getopts normally parses the positional parameters ($0 - $9), but if" -#~ msgstr "" -#~ "A getopts normál esetben a pozicionáló paramétereket ($0-$9) elemzi, de" - -#~ msgid "more arguments are given, they are parsed instead." -#~ msgstr "ha több paramétert kap akkor azokat használja helyettük." - -#~ msgid "Exec FILE, replacing this shell with the specified program." -#~ msgstr "" -#~ "FÁJL futtatása, kicseréli a parancsértelmezõt egy meghatározott programra." - -#~ msgid "If FILE is not specified, the redirections take effect in this" -#~ msgstr "" -#~ "Ha a FÁJL nincs meghatározva akkor az átírányítás effektus lép életbe" - -#~ msgid "shell. If the first argument is `-l', then place a dash in the" -#~ msgstr "" -#~ "a parancsértelmezõn. Ha az elsõ paraméter '-l' akkor egy gondolatjelet" - -#~ msgid "zeroth arg passed to FILE, as login does. If the `-c' option" -#~ msgstr "" -#~ "rak a FÁJL nulladik paraméterébe mint ahogy a login teszi. Ha a '-c' " -#~ "opciót" - -#~ msgid "is supplied, FILE is executed with a null environment. The `-a'" -#~ msgstr "alkalmazza akkor a FÁJL üres környezeti változókkal indul. A '-a'" - -#~ msgid "option means to make set argv[0] of the executed process to NAME." -#~ msgstr "" -#~ "opció azt jeleni, hogy a futtatott mûvelet NEVét az argv[0]-ra állítja." - -#~ msgid "If the file cannot be executed and the shell is not interactive," -#~ msgstr "Ha nem lehet a fájlt futtatni és a parancsértelmezõ nem interaktív," - -#~ msgid "then the shell exits, unless the variable \"no_exit_on_failed_exec\"" -#~ msgstr "" -#~ "akkor a parancsértelmezõ kilép, hacsak nem a \"no_exit_on_failed_exec\"" - -#~ msgid "is set." -#~ msgstr "változó nincs beállítva." - -#~ msgid "is that of the last command executed." -#~ msgstr "" -#~ "visszatérési érték az utoljára futtatott parancs vissza térési értéke " -#~ "lesz." - -#~ msgid "" -#~ "FIRST and LAST can be numbers specifying the range, or FIRST can be a" -#~ msgstr "" -#~ "Az elsõ és az utolsó lehet egy tömb kezdõ és befejezõ értéke, vagy az elsõ" - -#~ msgid "string, which means the most recent command beginning with that" -#~ msgstr "lehet egy sztring ami a legutolsó parancs elsõ sztringjét" - -#~ msgid "string." -#~ msgstr "jelenti." - -#~ msgid "" -#~ " -e ENAME selects which editor to use. Default is FCEDIT, then EDITOR," -#~ msgstr "" -#~ " -e ENÉV kiválasztja a felhasznált szerkesztõt . Alapból a vi-t " -#~ "használja" - -#~ msgid "" -#~ " then the editor which corresponds to the current readline editing" -#~ msgstr " " - -#~ msgid " mode, then vi." -#~ msgstr " " - -#~ msgid " -l means list lines instead of editing." -#~ msgstr " -l a szerkesztés helyet csak megjeleníti az elemeket." - -#~ msgid " -n means no line numbers listed." -#~ msgstr " -n nem írja ki a sorok számát." - -#~ msgid "" -#~ " -r means reverse the order of the lines (making it newest listed " -#~ "first)." -#~ msgstr " -r visszafele rendezi a sorokat (a legújabb elem lesz az elsõ)." - -#~ msgid "With the `fc -s [pat=rep ...] [command]' format, the command is" -#~ msgstr "" -#~ "A fc -s [pat=rep ...] [command]' formával, az újra lefuttatott parancs" - -#~ msgid "re-executed after the substitution OLD=NEW is performed." -#~ msgstr "behelyettesíti a saját helyére az utasítást." - -#~ msgid "A useful alias to use with this is r='fc -s', so that typing `r cc'" -#~ msgstr "Egy hasznos alias az r='fc -s', így amikor begépeli a 'r cc'" - -#~ msgid "runs the last command beginning with `cc' and typing `r' re-executes" -#~ msgstr "" -#~ "lefuttatja az utolsó 'cc'-vel kezdõdõ parancsot és amikor beírja az 'r'-t " - -#~ msgid "JOB_SPEC is not present, the shell's notion of the current job is" -#~ msgstr "" -#~ "Ha munka_folyamat nincs meghatározva akkor a jelenlegi munka folyamatot" - -#~ msgid "used." -#~ msgstr "használja." - -#~ msgid "Place JOB_SPEC in the background, as if it had been started with" -#~ msgstr "" -#~ "nka_folyamat-ot az háttérbe helyezi mintha '&' jellel indította volna" - -#~ msgid "`&'. If JOB_SPEC is not present, the shell's notion of the current" -#~ msgstr "Ha a munkafolyamat nincs meghatározva akkor" - -#~ msgid "job is used." -#~ msgstr "a jelenlegi munkafolyamatot használja." - -#~ msgid "For each NAME, the full pathname of the command is determined and" -#~ msgstr "Meghatározza az összes név teljes elérési útvonalát és megjegyzi." - -#~ msgid "remembered. If the -p option is supplied, PATHNAME is used as the" -#~ msgstr "Ha a '-p' opciót használja akkor az ÚTVONAL-at veszi a NÉV teljes" - -#~ msgid "full pathname of NAME, and no path search is performed. The -r" -#~ msgstr "útvonalának és nem keresi az útvonalat. A '-r' opció" - -#~ msgid "option causes the shell to forget all remembered locations. If no" -#~ msgstr "felszólítja a parancsértelmezõt, hogy felejtse el az összes helyet." - -#~ msgid "" -#~ "arguments are given, information about remembered commands is displayed." -#~ msgstr "" -#~ "Ha nincs megadva paraméter akkor megjeleníti a megjegyzett parancsokat." - -#~ msgid "Display helpful information about builtin commands. If PATTERN is" -#~ msgstr "Hasznos információkat jelenít meg a beépített parancsokról. Ha a " - -#~ msgid "specified, gives detailed help on all commands matching PATTERN," -#~ msgstr "" -#~ "MINTA meg van határozva, akkor részletes segítséget ír ki az utasításról" - -#~ msgid "otherwise a list of the builtins is printed." -#~ msgstr "egyébként egy listát jelenít meg a beépített parancsokról." - -#~ msgid "Display the history list with line numbers. Lines listed with" -#~ msgstr "Sorszámozva megjeleníti az elõzményeket. A megváltoztatott sorokat" - -#~ msgid "with a `*' have been modified. Argument of N says to list only" -#~ msgstr "" -#~ "egy csillaggal(*) jelöli. Az N opció azt jelenti, hogy csak az utolsó" - -#~ msgid "the last N lines. The -c option causes the history list to be" -#~ msgstr "N sort mutatja meg. A '-c' opció eredménye, hogy az elõzmény" - -#~ msgid "" -#~ "cleared by deleting all of the entries. The `-w' option writes out the" -#~ msgstr "" -#~ "lista összes eleme törlõdik. A '-w' opció azonnal kiírja az elõzményeket" - -#~ msgid "" -#~ "current history to the history file; `-r' means to read the file and" -#~ msgstr "az aktuális listába, '-r' pedig beolvassa a fájlt" - -#~ msgid "append the contents to the history list instead. `-a' means" -#~ msgstr "és az új fájlt elemeit használja helyette. A '-a' hozzáfûzi" - -#~ msgid "to append history lines from this session to the history file." -#~ msgstr "az aktuális terminál sorait a teljes elõzmény fájlhoz." - -#~ msgid "Argument `-n' means to read all history lines not already read" -#~ msgstr "A '-n' opció beolvassa az egész elõzmény nem beolvasott sorait" - -#~ msgid "from the history file and append them to the history list. If" -#~ msgstr "ez elõzmények fájlból és hozzáfûzi az elõzmény listához." - -#~ msgid "FILENAME is given, then that is used as the history file else" -#~ msgstr "Ha fájlnevet is megadunk akkor azt a fájlt fogja használni" - -#~ msgid "if $HISTFILE has a value, that is used, else ~/.bash_history." -#~ msgstr "" -#~ "Ha nincs a $HISFILE-nak érték adva akkor a ~/.bash_history-t használja." - -#~ msgid "If the -s option is supplied, the non-option ARGs are appended to" -#~ msgstr "A '-s' opció hatására az ARG-ot hozzáfûzi" - -#~ msgid "the history list as a single entry. The -p option means to perform" -#~ msgstr "az elõzményekhez. A '-p' opció " - -#~ msgid "" -#~ "history expansion on each ARG and display the result, without storing" -#~ msgstr "végre hajtja ez összes ARG-ot és az eredményt megjeleníti" - -#~ msgid "anything in the history list." -#~ msgstr "anélkül, hogy bármit is beírna az elõzmény listába." - -#~ msgid "Lists the active jobs. The -l option lists process id's in addition" -#~ msgstr "" -#~ "Kilistázza az aktív munka folyamatokat. A '-l' opció kiírja feladat " -#~ "azonosított" - -#~ msgid "to the normal information; the -p option lists process id's only." -#~ msgstr "" -#~ "a normál információk mellé, a '-p' opció pedig csak a feladat azonosított " -#~ "írja ki." - -#~ msgid "" -#~ "If -n is given, only processes that have changed status since the last" -#~ msgstr "" -#~ "Ha a '-n' opciót használja akkor csak a változásokat írja ki ami az " -#~ "utolsó hívás " - -#~ msgid "" -#~ "notification are printed. JOBSPEC restricts output to that job. The" -#~ msgstr "után történt. A MUNKAFOLYAMAT meghatározzam, hogy melyiket írja ki." - -#~ msgid "-r and -s options restrict output to running and stopped jobs only," -#~ msgstr "" -#~ "A '-r' és a '-s' opciók csak a futó és a megállított folyamatokat írja ki" - -#~ msgid "respectively. Without options, the status of all active jobs is" -#~ msgstr "Minden opció nélkül megjeleníti az összes aktív munkafolyamatot" - -#~ msgid "" -#~ "printed. If -x is given, COMMAND is run after all job specifications" -#~ msgstr "" -#~ "A '-x' opció hatására a PARANCS lefutása után az összes munkafolyamat " - -#~ msgid "" -#~ "that appear in ARGS have been replaced with the process ID of that job's" -#~ msgstr "" -#~ "meghatározás ami az ARGS-ban feltûnik az kicserélõdik a munkafolyamat " -#~ "vezetõ" - -#~ msgid "process group leader." -#~ msgstr "feladat azonosítójára." - -#~ msgid "Removes each JOBSPEC argument from the table of active jobs." -#~ msgstr "Eltávolítja az összes MUNKAFOLYAMAT-ot az aktív folyamatok közül." - -#~ msgid "Send the processes named by PID (or JOB) the signal SIGSPEC. If" -#~ msgstr "A jeltípust(sigspec) küld a meghatározott PID-re vagy JOB-ra." - -#~ msgid "" -#~ "SIGSPEC is not present, then SIGTERM is assumed. An argument of `-l'" -#~ msgstr "" -#~ "Ha a jeltípus(sigpsec) nincs meghatározva akkor egy SIGTERM-et küld." - -#~ msgid "lists the signal names; if arguments follow `-l' they are assumed to" -#~ msgstr "" -#~ "A '-l' opció kilistázza a jelek neveit.Ha a '-l' opció paramétere egy" - -#~ msgid "be signal numbers for which names should be listed. Kill is a shell" -#~ msgstr "" -#~ "jel száma akkor a számhoz tartozó nevet írja ki. A kill két okból " -#~ "beépített" - -#~ msgid "builtin for two reasons: it allows job IDs to be used instead of" -#~ msgstr "utasítása a parancsértelmezõnek: a job azonosító helyet" - -#~ msgid "process IDs, and, if you have reached the limit on processes that" -#~ msgstr "és ha (esetleg) a feladatok elérik a maximális határt akkor nem" - -#~ msgid "" -#~ "you can create, you don't have to start a process to kill another one." -#~ msgstr "" -#~ "nem lehet új feladatot indítani(fõleg a kill) és így nem lehetséges " -#~ "kilõni semmit." - -#~ msgid "Each ARG is an arithmetic expression to be evaluated. Evaluation" -#~ msgstr "Minden ARG egy matematikai kifelyezés. Az érték" - -#~ msgid "is done in long integers with no check for overflow, though division" -#~ msgstr "lehet hosszú egész túlcsordulás ellenõrzés nélkül. A nullával való" - -#~ msgid "by 0 is trapped and flagged as an error. The following list of" -#~ msgstr "" -#~ "osztás megszakítja és hibát jelez.A következõ lista egy összefoglaló" - -#~ msgid "operators is grouped into levels of equal-precedence operators." -#~ msgstr "az operátorokról végrehajtás szerinti csoportosításban." - -#~ msgid "The levels are listed in order of decreasing precedence." -#~ msgstr "" -#~ "A listában végrehajtás szerint csökkenõ sorrendben vannak felsorolva." - -#~ msgid "\t-, +\t\tunary minus, plus" -#~ msgstr "\t-, +\t\tunáris mínusz, plusz" - -#~ msgid "\t!, ~\t\tlogical and bitwise negation" -#~ msgstr "\t!, ~\t\tlogikai és bitszintû negálás" - -#~ msgid "\t*, /, %\t\tmultiplication, division, remainder" -#~ msgstr "\t*, /, %\t\tszorzás, osztás, maradékképzés" - -#~ msgid "\t+, -\t\taddition, subtraction" -#~ msgstr "\t+, -\t\tösszeadása, kivonás" - -#~ msgid "\t<<, >>\t\tleft and right bitwise shifts" -#~ msgstr "\t<<, >>\t\tbitszintû balra és jobbra emelés" - -#~ msgid "\t<=, >=, <, >\tcomparison" -#~ msgstr "\t<=, >=, <, >\tösszehasonlítás" - -#~ msgid "\t==, !=\t\tequality, inequality" -#~ msgstr "\t==, !=\t\tegyenlõség ,egyenlõtlenség" - -#~ msgid "\t&\t\tbitwise AND" -#~ msgstr "\t&\t\tbitszintû ÉS" - -#~ msgid "\t^\t\tbitwise XOR" -#~ msgstr "\t^\t\tbitszintû kizáró-vagy" - -#~ msgid "\t|\t\tbitwise OR" -#~ msgstr "\t|\t\tbitszintû VAGY" - -#~ msgid "\t&&\t\tlogical AND" -#~ msgstr "\t&&\t\tlogikai ÉS" - -#~ msgid "\t||\t\tlogical OR" -#~ msgstr "\t||\t\tlogikai VAGY" - -#~ msgid "\texpr ? expr : expr" -#~ msgstr "\tkifelyezés ? kifelyezés : kifelyezés" - -#~ msgid "\t\t\tconditional expression" -#~ msgstr "\t\t\tfeltételes kifelyezés" - -#~ msgid "\t=, *=, /=, %=," -#~ msgstr "\t=, *=, /=, %=," - -#~ msgid "\t+=, -=, <<=, >>=," -#~ msgstr "\t+=, -=, <<=, >>=," - -#~ msgid "\t&=, ^=, |=\tassignment" -#~ msgstr "\t&=, ^=, |=\thozzárendelés" - -#~ msgid "is replaced by its value (coerced to a long integer) within" -#~ msgstr "behelyettesíthetõ a kifelyezésekben értékek helyére " - -#~ msgid "an expression. The variable need not have its integer attribute" -#~ msgstr "" -#~ "(hosszú egészre kiegészítve). A változónak nem lehet egész attribútuma" - -#~ msgid "turned on to be used in an expression." -#~ msgstr "beállítva, ha kifelyezésként kívánja használni." - -#~ msgid "Operators are evaluated in order of precedence. Sub-expressions in" -#~ msgstr "Az operátorok kiértékelési sorrendben. Az al-kifelyezések" - -#~ msgid "parentheses are evaluated first and may override the precedence" -#~ msgstr "" -#~ "elsõnek hívódnak meg és ezzel felülbírálhatják a teljes végre hajtási" - -#~ msgid "rules above." -#~ msgstr "szabályokat." - -#~ msgid "If the last ARG evaluates to 0, let returns 1; 0 is returned" -#~ msgstr "Ha az utolsó ARG értéke 0 akkor a visszatérési értéke 1, egyébként" - -#~ msgid "otherwise." -#~ msgstr "0 -val tér vissza." - -#~ msgid "One line is read from the standard input, and the first word is" -#~ msgstr "Egy sort beolvas a standard bemenetrõl és az elsõ szót" - -#~ msgid "" -#~ "assigned to the first NAME, the second word to the second NAME, and so" -#~ msgstr "" -#~ "hozzárendeli az elsõ NÉV-hez, a második szót hozzárendeli a második NÉV-" -#~ "hez" - -#~ msgid "" -#~ "on, with leftover words assigned to the last NAME. Only the characters" -#~ msgstr "" -#~ "és így tovább egészen addig míg el nem éri az utolsó NEVet. Abban az " -#~ "esetben" - -#~ msgid "found in $IFS are recognized as word delimiters. The return code is" -#~ msgstr "" -#~ "ha a $IFS egy karaktert talál akkor azt szóhatárolóként fogja fel. A " -#~ "visszatérési" - -#~ msgid "" -#~ "zero, unless end-of-file is encountered. If no NAMEs are supplied, the" -#~ msgstr "" -#~ "érték 0, hacsak nem kap fájl vége jelet.Ha a NÉV nincs meghatározva, akkor" - -#~ msgid "" -#~ "line read is stored in the REPLY variable. If the -r option is given," -#~ msgstr "" -#~ "a beolvasott sorok a REPLY változóban tárolódnak. A '-r' opció hatására" - -#~ msgid "this signifies `raw' input, and backslash escaping is disabled. If" -#~ msgstr "" -#~ "a bemenetet, mint nyers bemenet használja és a backslash karaktereket " -#~ "letiltja." - -#~ msgid "the `-p' option is supplied, the string supplied as an argument is" -#~ msgstr "" -#~ "Ha a '-p' opció sztring paraméterét kiírja (újsor karakter nélkül) a " -#~ "kimenetre" - -#~ msgid "" -#~ "output without a trailing newline before attempting to read. If -a is" -#~ msgstr "mielõtt még az olvasásba belekezdene. A '-a' opció hatására" - -#~ msgid "" -#~ "supplied, the words read are assigned to sequential indices of ARRAY," -#~ msgstr "a beolvasott szavak egy indexelt tömb elemeiket veszi fel, nullával" - -#~ msgid "starting at zero. If -e is supplied and the shell is interactive," -#~ msgstr "" -#~ "kezdõdõen. A '-e' opció alkalmazása és a parancsértelmezõ párbeszéd módja" - -#~ msgid "readline is used to obtain the line." -#~ msgstr "hatására a 'readline'-ból veszi a feldolgozási sort." - -#~ msgid "is omitted, the return status is that of the last command." -#~ msgstr "" -#~ "Ha az N értéke nincs megadva akkor a visszatérési értéket az utolsó " -#~ "parancsból veszi." - -#~ msgid " -a Mark variables which are modified or created for export." -#~ msgstr "" -#~ " -a Megjelöli a változót úgy mintha az export módosította vagy " -#~ "létrehozta volna." - -#~ msgid " -b Notify of job termination immediately." -#~ msgstr " -b Azonnal megszakítja a munkafolyamatot." - -#~ msgid " -e Exit immediately if a command exits with a non-zero status." -#~ msgstr " -e Kilép azonnal ha a parancs nem nulla állapotban van." - -#~ msgid " -f Disable file name generation (globbing)." -#~ msgstr " -f Letiltja a fájlnév generálást (globbing)." - -#~ msgid " -h Remember the location of commands as they are looked up." -#~ msgstr " -h Megjegyzi a parancs helyét amit megtalált." - -#~ msgid "" -#~ " -i Force the shell to be an \"interactive\" one. Interactive shells" -#~ msgstr "" -#~ " -i Kierõlteti a parancsértelmezõtõl a párbeszédes üzemmódot. Ez a " - -#~ msgid " always read `~/.bashrc' on startup." -#~ msgstr " mód mindig a '~/.bashrc' használja indulásnál." - -#~ msgid " -k All assignment arguments are placed in the environment for a" -#~ msgstr "" -#~ " -k Az összes hozzárendelt paraméter elhelyezi a környezeti változok " -#~ "között" - -#~ msgid " command, not just those that precede the command name." -#~ msgstr " nem csak azt ami a parancs nevét megelõzi." - -#~ msgid " -m Job control is enabled." -#~ msgstr " -m A munkafolyamat ellenõrzés engedélyezése." - -#~ msgid " -n Read commands but do not execute them." -#~ msgstr " -n Beolvassa a parancsok, de nem hajtja õket végre." - -#~ msgid " -o option-name" -#~ msgstr " -o opció neve" - -#~ msgid " Set the variable corresponding to option-name:" -#~ msgstr "" -#~ " A változok beállításához használhatók a következõ opció nevek:" - -#~ msgid " allexport same as -a" -#~ msgstr " allexport ugyanaz mint a -a" - -#~ msgid " braceexpand same as -B" -#~ msgstr " braceexpand ugyanaz mint a -B" - -#~ msgid " emacs use an emacs-style line editing interface" -#~ msgstr " emacs emacs stílusú sor szerkesztés használata" - -#~ msgid " errexit same as -e" -#~ msgstr " errexit ugyanaz mint a -e" - -#~ msgid " hashall same as -h" -#~ msgstr " hashall ugyanaz mint a -h" - -#~ msgid " histexpand same as -H" -#~ msgstr " histexpand ugyanaz mint a -H" - -#~ msgid " ignoreeof the shell will not exit upon reading EOF" -#~ msgstr "" -#~ " ignoreeof a parancsértelmezõ nem lép ki amíg nem kap EOF " -#~ "jelet" - -#~ msgid " interactive-comments" -#~ msgstr " interactive-comments" - -#~ msgid "" -#~ " allow comments to appear in interactive commands" -#~ msgstr "" -#~ " engedélyezi a megjegyzéseket a párbeszédes parancsoknál" - -#~ msgid " keyword same as -k" -#~ msgstr " keyword ugyanaz mint a -k" - -#~ msgid " monitor same as -m" -#~ msgstr " monitor ugyanaz mint a -m" - -#~ msgid " noclobber same as -C" -#~ msgstr " noclobber ugyanaz mint a -C" - -#~ msgid " noexec same as -n" -#~ msgstr " noexec ugyanaz mint a -n" - -#~ msgid " noglob same as -f" -#~ msgstr " noglob ugyanaz mint a -f" - -#~ msgid " notify save as -b" -#~ msgstr " notify ugyanaz mint a -b" - -#~ msgid " nounset same as -u" -#~ msgstr " nounset ugyanaz mint a -u" - -#~ msgid " onecmd same as -t" -#~ msgstr " onecmd ugyanaz mint a -t" - -#~ msgid " physical same as -P" -#~ msgstr " physical ugyanaz mint a -P" - -#~ msgid "" -#~ " posix change the behavior of bash where the default" -#~ msgstr "" -#~ " posix megváltoztatja a bash viselkedését ott ahol az " -#~ "alap" - -#~ msgid "" -#~ " operation differs from the 1003.2 standard to" -#~ msgstr " mód különbözik a 1003.2 szabványtól" - -#~ msgid " match the standard" -#~ msgstr " az aktuális szabvány" - -#~ msgid " privileged same as -p" -#~ msgstr " privileged ugyanaz mint a -p" - -#~ msgid " verbose same as -v" -#~ msgstr " verbose ugyanaz mint a -v" - -#~ msgid " vi use a vi-style line editing interface" -#~ msgstr " vi vi stílusú sor szerkesztést használ" - -#~ msgid " xtrace same as -x" -#~ msgstr " xtrace ugyanaz mint a -x" - -#~ msgid "" -#~ " -p Turned on whenever the real and effective user ids do not match." -#~ msgstr "" -#~ " -p Bekapcsolja még akkor is ha a valós és az effektív felhasználó " -#~ "azonosító" - -#~ msgid " Disables processing of the $ENV file and importing of shell" -#~ msgstr "" -#~ " különbözik. Letiltja $ENV fájl feldolgozását és a parancsértelmezõbe " -#~ "építését" - -#~ msgid "" -#~ " functions. Turning this option off causes the effective uid and" -#~ msgstr " Beállítja az effektív felhasználó és csoport azonosítót a " - -#~ msgid " gid to be set to the real uid and gid." -#~ msgstr " valós felhasználó és csoport azonosítóra." - -#~ msgid " -t Exit after reading and executing one command." -#~ msgstr " -t Kilép az olvasás és egy parancs végrehajtása után." - -#~ msgid " -u Treat unset variables as an error when substituting." -#~ msgstr "" -#~ " -u Úgy kezeli a leállított változókat mintha hibás lenne a " -#~ "behelyettesítés." - -#~ msgid " -v Print shell input lines as they are read." -#~ msgstr " -v Kiírja a parancsértelmezõ bemenetét úgy ahogy beolvasta." - -#~ msgid " -x Print commands and their arguments as they are executed." -#~ msgstr "" -#~ " -x Kiírja a parancsokat és a paramétereiket úgy ahogy lefuttatta " -#~ "õket." - -#~ msgid " -B the shell will perform brace expansion" -#~ msgstr " -B a parancsértelmezõ elõkészíti a közös bõvítményeket" - -#~ msgid " -H Enable ! style history substitution. This flag is on" -#~ msgstr " -H Engedélyezés a ! stílusú elõzmény behelyettesítés. Ez a jel" - -#~ msgid " by default." -#~ msgstr " alapból be van kapcsolva." - -#~ msgid " -C If set, disallow existing regular files to be overwritten" -#~ msgstr " -C Ha be van állítva akkor a hagyományos fájl felülírása" - -#~ msgid " by redirection of output." -#~ msgstr " a kimenet átirányításával." - -#~ msgid " -P If set, do not follow symbolic links when executing commands" -#~ msgstr "" -#~ " -P Ha be van állítva akkor nem fogja követni a szimbolikus linkeket " -#~ "amikor" - -#~ msgid " such as cd which change the current directory." -#~ msgstr "" -#~ "elindított program mint a 'cd' megpróbálja meg változtatni az aktuális " -#~ "könyvtárat." - -#~ msgid "Using + rather than - causes these flags to be turned off. The" -#~ msgstr "Ha a '-' helyet '+' használ akkor a jelzõt kikapcsolja." - -#~ msgid "flags can also be used upon invocation of the shell. The current" -#~ msgstr "A jelzõket még lehet használni a parancsértelmezõ meghívására.A " - -#~ msgid "" -#~ "set of flags may be found in $-. The remaining n ARGs are positional" -#~ msgstr "" -#~ "jelenlegi jelzõ beállításokat a $- tárolja. A maradék n ARG a pozicionáló" - -#~ msgid "parameters and are assigned, in order, to $1, $2, .. $n. If no" -#~ msgstr "paraméterekhez rendeli hozzá ($1, $2, $3 ...$n) Ha nem" - -#~ msgid "ARGs are given, all shell variables are printed." -#~ msgstr "nem adunk semmilyen opciót meg akkor az összes változó kiírja." - -#~ msgid "For each NAME, remove the corresponding variable or function. Given" -#~ msgstr "Az összes NÉV változót vagy függvényt letörli" - -#~ msgid "the `-v', unset will only act on variables. Given the `-f' flag," -#~ msgstr "A '-v' használatakor csak a változókat állítja le.A '-f' pedig" - -#~ msgid "unset will only act on functions. With neither flag, unset first" -#~ msgstr "csak a funkciókat.Opciók nélkül az unset elõször megpróbálja a " - -#~ msgid "tries to unset a variable, and if that fails, then tries to unset a" -#~ msgstr "" -#~ "a változók leállítását és ha nem sikerül akkor megpróbálja a funkciókat " -#~ "is." - -#~ msgid "" -#~ "function. Some variables (such as PATH and IFS) cannot be unset; also" -#~ msgstr "Néhány kiemelt változót (pl PATH vagy IFS) nem lehet letörölni" - -#~ msgid "see readonly." -#~ msgstr "ezek csak olvashatók." - -#~ msgid "NAMEs are marked for automatic export to the environment of" -#~ msgstr "" -#~ "A NEVET megjelöli automatikus exportálásra a késõbb elindított parancs" - -#~ msgid "subsequently executed commands. If the -f option is given," -#~ msgstr "környezeti változóiként." - -#~ msgid "the NAMEs refer to functions. If no NAMEs are given, or if `-p'" -#~ msgstr "" -#~ "A '-f' besorolja a NEVET a funkciók közé. Ha nem adja meg a nevet vagy" - -#~ msgid "is given, a list of all names that are exported in this shell is" -#~ msgstr "" -#~ "'-p' -t használja akkor kiírja az összes nevet amit exportáltak ebben a " -#~ "parancs-" - -#~ msgid "printed. An argument of `-n' says to remove the export property" -#~ msgstr "értelmezõben. A '-n' hatására eltávolítja a NEVET exportálandók" - -#~ msgid "from subsequent NAMEs. An argument of `--' disables further option" -#~ msgstr "közül. A '--' paramétere letiltja a következõ opció" - -#~ msgid "processing." -#~ msgstr "feldolgozását." - -#~ msgid "" -#~ "The given NAMEs are marked readonly and the values of these NAMEs may" -#~ msgstr "" -#~ "Az átadott NEVET csak olvashatóvá teszi és ettõl kezdve nem lehet meg" - -#~ msgid "not be changed by subsequent assignment. If the -f option is given," -#~ msgstr "változtatni a hozzárendeléseket. " - -#~ msgid "then functions corresponding to the NAMEs are so marked. If no" -#~ msgstr "A '-f' hatására a funkciónak megfelelõ NEVET megjelöli. Ha" - -#~ msgid "" -#~ "arguments are given, or if `-p' is given, a list of all readonly names" -#~ msgstr "" -#~ "opciók nélkül vagy a '-p'-vel indítjuk el akkor egy listát kapunk a csak" - -#~ msgid "" -#~ "is printed. An argument of `-n' says to remove the readonly property" -#~ msgstr "" -#~ "olvasható funkciókról.A '-n' hatására eltávolítja a NEVET csak olvashatók" - -#~ msgid "from subsequent NAMEs. The `-a' option means to treat each NAME as" -#~ msgstr "közül.A '-a' opció az összes NEVET tömb változóként kezel." - -#~ msgid "an array variable. An argument of `--' disables further option" -#~ msgstr "A '--' paramétere letiltja a következõ opció feldolgozását." - -#~ msgid "not given, it is assumed to be 1." -#~ msgstr "értékét nem adjuk meg, akkor egyel csökkenti." - -#~ msgid "Read and execute commands from FILENAME and return. The pathnames" -#~ msgstr "" -#~ "Beolvas és végrehajtja a parancsokat a FÁJLNÉV-bõl és visszatér.A $PATH" - -#~ msgid "in $PATH are used to find the directory containing FILENAME." -#~ msgstr "változóban található útvonalon próbálja meg keresi a FÁJLNEVET." - -#~ msgid "Suspend the execution of this shell until it receives a SIGCONT" -#~ msgstr "" -#~ "Felfüggeszti ennek a parancsértelmezõnek a futtatását amíg egy SIGCONT" - -#~ msgid "signal. The `-f' if specified says not to complain about this" -#~ msgstr "" -#~ "jelet nem kap.A '-f' azt jelenti, hogy nem fog szólni, ha egy " -#~ "bejelentkezett" - -#~ msgid "being a login shell if it is; just suspend anyway." -#~ msgstr "parancsértelmezõt próbál felfüggeszteni, mindenképp felfüggeszt." - -#~ msgid "Exits with a status of 0 (trueness) or 1 (falseness) depending on" -#~ msgstr "" -#~ "Kilép 0 státusszal (igaz) vagy 1-el (hiba) a KIFELYEZÉS eredményétõl" - -#~ msgid "the evaluation of EXPR. Expressions may be unary or binary. Unary" -#~ msgstr "függõen. A KIFELYEZÉS lehet unáris vagy bináris. Az unáris " - -#~ msgid "expressions are often used to examine the status of a file. There" -#~ msgstr "" -#~ "kifelyezéseket gyakran használják fájlok helyzetének a vizsgálatára." - -#~ msgid "are string operators as well, and numeric comparison operators." -#~ msgstr "Vagy ott ahol a sztingeket kell összehasonlítani számokkal." - -#~ msgid "File operators:" -#~ msgstr "Fájl operátorok:" - -#~ msgid " -b FILE True if file is block special." -#~ msgstr " -b FÁJL Igaz ha a FÁJL egy speciális blokk fájl." - -#~ msgid " -c FILE True if file is character special." -#~ msgstr " -c FÁJL Igaz ha a FÁJL egy speciális karakter fájl" - -#~ msgid " -d FILE True if file is a directory." -#~ msgstr " -d FÁJL Igaz ha a FÁJL egy könyvtár" - -#~ msgid " -e FILE True if file exists." -#~ msgstr " -e FÁJL Igaz ha a FÁJL létezik." - -#~ msgid " -f FILE True if file exists and is a regular file." -#~ msgstr " -f FÁJL Igaz ha a FÁJL létezik és általános fájl." - -#~ msgid " -g FILE True if file is set-group-id." -#~ msgstr "" -#~ " -g FÁJL Igaz ha a FÁJL csoport azonosítója be van állítva." - -#~ msgid " -h FILE True if file is a symbolic link. Use \"-L\"." -#~ msgstr "" -#~ " -h FÁJL Igaz ha a FÁJL egy szimbolikus link. A \"-L\" " -#~ "használatával." - -#~ msgid " -L FILE True if file is a symbolic link." -#~ msgstr " -L FÁJL Igaz ha a FÁJL egy szimbolikus link." - -#~ msgid " -k FILE True if file has its \"sticky\" bit set." -#~ msgstr "" -#~ " -k FÁJL Igaz ha a FÁJL-on be van állítva a rögzített bit." - -#~ msgid " -p FILE True if file is a named pipe." -#~ msgstr " -p FÁJL Igaz ha a FÁJL egy csõ(pipe)." - -#~ msgid " -r FILE True if file is readable by you." -#~ msgstr " -r FÁJL Igaz ha a FÁJL ön által olvasható." - -#~ msgid " -s FILE True if file exists and is not empty." -#~ msgstr " -s FÁJL Igaz ha a FÁJL létezik és nem üres." - -#~ msgid " -S FILE True if file is a socket." -#~ msgstr " -S FÁJL Igaz ha a FÁJL egy socket." - -#~ msgid " -t FD True if FD is opened on a terminal." -#~ msgstr " -t FD Igaz ha az FD megnyitották egy terminálon." - -#~ msgid " -u FILE True if the file is set-user-id." -#~ msgstr "" -#~ " -u FÁJL Igaz ha a FÁJL-on a felhasználó azonosító be van " -#~ "állítva." - -#~ msgid " -w FILE True if the file is writable by you." -#~ msgstr " -w FÁJL Igaz ha a FÁJL-t ön tudja írni." - -#~ msgid " -x FILE True if the file is executable by you." -#~ msgstr " -x FÁJL Igaz ha a FÁJL-t tudja futtatni." - -#~ msgid " -O FILE True if the file is effectively owned by you." -#~ msgstr " -O FÁJL Igaz ha a FÁJL-t effektíve ön birtokolja." - -#~ msgid "" -#~ " -G FILE True if the file is effectively owned by your group." -#~ msgstr "" -#~ " -G FÁJL Igaz ha a FÁJL az ön csoportja effektíve birtokolja." - -#~ msgid " FILE1 -nt FILE2 True if file1 is newer than (according to" -#~ msgstr " FÁJL1 -nt FÁJL2 Igaz ha a FÁJL1 újabb mint (módosítási dátum " - -#~ msgid " modification date) file2." -#~ msgstr " szerint) FÁJL2." - -#~ msgid " FILE1 -ot FILE2 True if file1 is older than file2." -#~ msgstr " FÁJL1 -bot FÁJL2 Igaz ha a FÁJL1 régebbi, mint a FÁJL2." - -#~ msgid " FILE1 -ef FILE2 True if file1 is a hard link to file2." -#~ msgstr " FÁJL1 -ef FÁJL2 Igaz ha a FÁJL1 egy hard link a FÁJL2-re." - -#~ msgid "String operators:" -#~ msgstr "Sztring operátorok:" - -#~ msgid " -z STRING True if string is empty." -#~ msgstr " -z SZTRING Igaz .ha a SZTRING üres." - -#~ msgid " -n STRING" -#~ msgstr " -n SZTRING" - -#~ msgid " STRING True if string is not empty." -#~ msgstr " SZTRING Igaz .ha a SZTRING nem üres" - -#~ msgid " STRING1 = STRING2" -#~ msgstr " SZTRING1 = SZTRING2" - -#~ msgid " True if the strings are equal." -#~ msgstr " Igaz ha a két sztring azonos." - -#~ msgid " STRING1 != STRING2" -#~ msgstr " SZTRING1 != SZTRING2" - -#~ msgid " True if the strings are not equal." -#~ msgstr " Igaz ha a két sztring nem azonos." - -#~ msgid " STRING1 < STRING2" -#~ msgstr " SZTRING1 < SZTRING2" - -#~ msgid "" -#~ " True if STRING1 sorts before STRING2 lexicographically" -#~ msgstr "" -#~ " Igaz ha a SZTRING1 elõrébb van mint a SZTRING2 " -#~ "lexikálisan" - -#~ msgid " STRING1 > STRING2" -#~ msgstr " SZTRING1 > SZTRING2" - -#~ msgid "" -#~ " True if STRING1 sorts after STRING2 lexicographically" -#~ msgstr "" -#~ " Igaz ha a SZTRING1 hátrébb van mint a SZTRING2 " -#~ "lexikálisan" - -#~ msgid "Other operators:" -#~ msgstr "Egyéb operátorok:" - -#~ msgid " ! EXPR True if expr is false." -#~ msgstr " ! KIFELYEZÉS Igaz ha a KIFELYEZÉS." - -#~ msgid " EXPR1 -a EXPR2 True if both expr1 AND expr2 are true." -#~ msgstr " KIF1 -a KIF2 Igaz ha mindkét(ÉS) KIFelyezés igaz." - -#~ msgid " EXPR1 -o EXPR2 True if either expr1 OR expr2 is true." -#~ msgstr " KIF1 -a KIF2 Igaz ha valamelyik(VAGY) KIFelyezés igaz." - -#~ msgid " arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne," -#~ msgstr " arg1 OP arg2 Számtani összehasonlítások. OP lehet -eq, -ne," - -#~ msgid " -lt, -le, -gt, or -ge." -#~ msgstr " -lt, -le, -gt, vagy -ge közül bármelyik." - -#~ msgid "Arithmetic binary operators return true if ARG1 is equal, not-equal," -#~ msgstr "" -#~ "A bináris számtani operátorok igaz értékel térnek vissza ha az ARG1 " -#~ "egyenlõ" - -#~ msgid "" -#~ "less-than, less-than-or-equal, greater-than, or greater-than-or-equal" -#~ msgstr "" -#~ "nem egyenlõ, kevesebb mint, kevesebb vagy egyenlõ, nagyobb mint vagy" - -#~ msgid "than ARG2." -#~ msgstr "nagyobb vagy egyenlõ mint az ARG2." - -#~ msgid "This is a synonym for the \"test\" builtin, but the last" -#~ msgstr "" -#~ "Ez egy szinonimája a \"test\" beépített utasításnak, de az utolsó " -#~ "paraméter" - -#~ msgid "the shell." -#~ msgstr "a feladatot futtassa ezen a parancsértelmezõn." - -#~ msgid "The command ARG is to be read and executed when the shell receives" -#~ msgstr "" -#~ "Az ARG-ban meghatározott parancsot futtatja le ha a parancsértelmezõ egy" - -#~ msgid "signal(s) SIGNAL_SPEC. If ARG is absent all specified signals are" -#~ msgstr "SIGNAL_SCEP jelet kap.Ha az ARG-ot elhagyjuk akkor az összes jel" - -#~ msgid "reset to their original values. If ARG is the null string each" -#~ msgstr "" -#~ "vissza áll az eredeti értékére. Ha az ARG egy üres sztring akkor a " -#~ "parancsértelmezõ" - -#~ msgid "SIGNAL_SPEC is ignored by the shell and by the commands it invokes." -#~ msgstr "és a parancsok SIGNAL_SPEC hívásait figyelmen kívül hagyja." - -#~ msgid "If SIGNAL_SPEC is EXIT (0) the command ARG is executed on exit from" -#~ msgstr "" -#~ "Ha egy SIGNAL_SPEC EXIT(0) ARG-ot kap akkor kilép az aktuális parancs-" - -#~ msgid "the shell. If SIGNAL_SPEC is DEBUG, ARG is executed after every" -#~ msgstr "értelmezõbõl.Ha a SIGNAL_SPEC értéke DEBUG akkor az ARG minden" - -#~ msgid "command. If ARG is `-p' then the trap commands associated with" -#~ msgstr "parancs után le fut. Ha az ARG egy '-p' akkor megjeleníti az összes" - -#~ msgid "each SIGNAL_SPEC are displayed. If no arguments are supplied or if" -#~ msgstr "SIGNAL_SPEC hozzárendelést. Ha opciók nélkül használja vagy" - -#~ msgid "only `-p' is given, trap prints the list of commands associated with" -#~ msgstr "" -#~ "csak a '-p'-t akkor a trap kiírja a hozzárendelt parancsok listáját az " - -#~ msgid "" -#~ "each signal number. SIGNAL_SPEC is either a signal name in <signal.h>" -#~ msgstr "" -#~ "összes jel számával. A SIGNAL_SPEC elfogadja a <signal.h>-ban " -#~ "meghatározott" - -#~ msgid "" -#~ "or a signal number. `trap -l' prints a list of signal names and their" -#~ msgstr "" -#~ "neveket vagy számukat. A 'trap -l' kiírja a jelek neveit és a " -#~ "hozzátartozó " - -#~ msgid "corresponding numbers. Note that a signal can be sent to the shell" -#~ msgstr "" -#~ "értékeket.Megjegyzés: a parancsértelemzõnek a \"kill -signal $$\" -al " - -#~ msgid "with \"kill -signal $$\"." -#~ msgstr "lehet küldeni." - -#~ msgid "For each NAME, indicate how it would be interpreted if used as a" -#~ msgstr "Az összes NÉVrõl hogyan értelmezi ha parancsértelmezõ amikor" - -#~ msgid "If the -t option is used, returns a single word which is one of" -#~ msgstr "" -#~ "Ha a '-t' opciót használja akkor az eredmény a következõ szavak egyike" - -#~ msgid "" -#~ "`alias', `keyword', `function', `builtin', `file' or `', if NAME is an" -#~ msgstr "" -#~ "`alias', `keyword', `function', `builtin', `file' vagy `', ha a NÉV egy" - -#~ msgid "" -#~ "alias, shell reserved word, shell function, shell builtin, disk file," -#~ msgstr "" -#~ "alias, parancsértelmezõ által fenntartott szó, parancsértelmezõ funkció, " - -#~ msgid "or unfound, respectively." -#~ msgstr "" -#~ "beépített utasítás, egy fájl a lemezen vagy ha nincs meg,sorrendben." - -#~ msgid "If the -p flag is used, either returns the name of the disk file" -#~ msgstr "" -#~ "Ha a '-p'-t használja vissza tér a lemezen található fájl pontos helyével" - -#~ msgid "that would be executed, or nothing if -t would not return `file'." -#~ msgstr "vagy ha nem ad vissza semmit akkor az nem egy fájl." - -#~ msgid "If the -a flag is used, displays all of the places that contain an" -#~ msgstr "" -#~ "A '-a' opció hatására megjeleníti az összes helyet ahol a megtalálható" - -#~ msgid "" -#~ "executable named `file'. This includes aliases and functions, if and" -#~ msgstr "" -#~ "mint futtatható fájl. Ez magában foglalja a aliasokat és a funkciókat " - -#~ msgid "only if the -p flag is not also used." -#~ msgstr "csak akkor ha '-p'-t nem használja." - -#~ msgid "Type accepts -all, -path, and -type in place of -a, -p, and -t," -#~ msgstr "A type elfogadja -all, -path, és a -type szavakat -a, -p, és -t," - -#~ msgid "respectively." -#~ msgstr "helyet." - -#~ msgid "Ulimit provides control over the resources available to processes" -#~ msgstr "" -#~ "Az ulimit teljes ellenõrzést biztosít a parancsértelmezõ által elindított " -#~ "programok" - -#~ msgid "started by the shell, on systems that allow such control. If an" -#~ msgstr "elõforrásai felet, ami által az egész rendszer lehet szabályozni." - -#~ msgid "option is given, it is interpreted as follows:" -#~ msgstr "A következõ lista bemutatja az elérhetõ opciókat:" - -#~ msgid " -S\tuse the `soft' resource limit" -#~ msgstr " -S\t \"lágy\" erõforrás korlátozást használ" - -#~ msgid " -H\tuse the `hard' resource limit" -#~ msgstr " -H\t \"kemény\" erõforrás korlátozást használ" - -#~ msgid " -a\tall current limits are reported" -#~ msgstr " -a\tmegjeleníti az összes korlátozás" - -#~ msgid " -c\tthe maximum size of core files created" -#~ msgstr " -c\ta maximálisan létrehozható core fájl mérete" - -#~ msgid " -d\tthe maximum size of a process's data segment" -#~ msgstr " -d maximális mérete feladatok adat szegmensének" - -#~ msgid " -m\tthe maximum resident set size" -#~ msgstr " -m\ta maximális bennmaradó(rezidest) rész mérete" - -#~ msgid " -s\tthe maximum stack size" -#~ msgstr " -s\ta verem maximális mérete" - -#~ msgid " -t\tthe maximum amount of cpu time in seconds" -#~ msgstr " -t\ta maximálisan felhasználható cpu idõ másodpercben" - -#~ msgid " -f\tthe maximum size of files created by the shell" -#~ msgstr "" -#~ " -f\t a parancsértelmezõ által létrehozható fájlok maximális mérete" - -#~ msgid " -p\tthe pipe buffer size" -#~ msgstr " -p\ta csõ(pipe) puffer mérete" - -#~ msgid " -n\tthe maximum number of open file descriptors" -#~ msgstr " -n\ta maximálisan megnyitható fájl leírók száma" - -#~ msgid " -u\tthe maximum number of user processes" -#~ msgstr " -u\ta felhasználó által maximálisan elindítható feladatok száma" - -#~ msgid " -v\tthe size of virtual memory" -#~ msgstr " -v\ta virtuális memória mérete" - -#~ msgid "If LIMIT is given, it is the new value of the specified resource." -#~ msgstr "Ha a HATÁR meghatározza akkor azaz értéket használja az erõforrás." - -#~ msgid "Otherwise, the current value of the specified resource is printed." -#~ msgstr "Egyébként megjeleníti a jelenlegi értékét az elõforrásnak." - -#~ msgid "If no option is given, then -f is assumed. Values are in 1k" -#~ msgstr "" -#~ "Ha opció nélkül indításhoz a '-f' van hozzárendelve.Az értékek Kilobájtban" - -#~ msgid "increments, except for -t, which is in seconds, -p, which is in" -#~ msgstr "" -#~ "értendõ, kivétel a '-a' értéke mert az másodpercet jelent és a '-p' " -#~ "amelyik" - -#~ msgid "increments of 512 bytes, and -u, which is an unscaled number of" -#~ msgstr "512 bájtot. A '-u' pedig a folyamatok száma darabban " - -#~ msgid "processes." -#~ msgstr "kihelyezve." - -#~ msgid "" -#~ "The user file-creation mask is set to MODE. If MODE is omitted, or if" -#~ msgstr "" -#~ "A felhasználó fájl létrehozási MÓDJÁT állíthatjuk be. Ha a MÓDot elhagyjuk" - -#~ msgid "" -#~ "`-S' is supplied, the current value of the mask is printed. The `-S'" -#~ msgstr "" -#~ "vagy a '-S' opciót használjuk akkor a jelenlegi maskot írja ki. A '-S' " -#~ "opció " - -#~ msgid "" -#~ "option makes the output symbolic; otherwise an octal number is output." -#~ msgstr "" -#~ "kimenete a szimbolikus jelentést írja ki egyébként nyolcas " -#~ "számrendszerben kódolva" - -#~ msgid "If MODE begins with a digit, it is interpreted as an octal number," -#~ msgstr "" -#~ "írja ki.Ha a MÓD számmal kezdõdik akkor nyolcas számrendszerben kódolva" - -#~ msgid "" -#~ "otherwise it is a symbolic mode string like that accepted by chmod(1)." -#~ msgstr "" -#~ "értelmezi egyébként a szimbolikus módokat használja mint a chmod(1)." - -#~ msgid "" -#~ "Wait for the specified process and report its termination status. If" -#~ msgstr "Várakozik egy meghatározott feladatra és jelentést készít róla." - -#~ msgid "N is not given, all currently active child processes are waited for," -#~ msgstr "" -#~ "Ha az N nincs meghatározva akkor a jelenleg aktív gyermek folyamatra vár" - -#~ msgid "and the return code is zero. N may be a process ID or a job" -#~ msgstr "" -#~ "és a visszatérési értéke 0. Az N lehet egy folyamat azonosító és egy " -#~ "munka-" - -#~ msgid "specification; if a job spec is given, all processes in the job's" -#~ msgstr "folyamat meghatározás, ha egy munkafolyamatot ad át akkor az összes" - -#~ msgid "pipeline are waited for." -#~ msgstr "folyamatára várakozik." - -#~ msgid "and the return code is zero. N is a process ID; if it is not given," -#~ msgstr "" -#~ "és a visszatérési értéke 0. Az N lehet egy folyamat azonosító ha ez nincs " - -#~ msgid "all child processes of the shell are waited for." -#~ msgstr "megadva a parancsértelmezõ összes gyermek feladatára várakozik." - -#~ msgid "The `for' loop executes a sequence of commands for each member in a" -#~ msgstr "A 'for' egy hurokban elindítja a parancsokat amíg az összes eleme" - -#~ msgid "" -#~ "list of items. If `in WORDS ...;' is not present, then `in \"$@\"' is" -#~ msgstr "" -#~ "végig nem futott. Ha az 'in SZAVAK..;'nincs jelen akkor a \"$@\" értékeit" - -#~ msgid "" -#~ "assumed. For each element in WORDS, NAME is set to that element, and" -#~ msgstr "használja fel.A SZAVAK összes elemét a NÉVhez rendeli és lefuttatja" - -#~ msgid "the COMMANDS are executed." -#~ msgstr "a PARANCSOKAT." - -#~ msgid "The WORDS are expanded, generating a list of words. The" -#~ msgstr "SZAVAK kiválasztása, lista generálása a szavakból" - -#~ msgid "set of expanded words is printed on the standard error, each" -#~ msgstr "Az összegyûjtõt szavakat a standard hibakimenetre küldi, mindegyik" - -#~ msgid "preceded by a number. If `in WORDS' is not present, `in \"$@\"'" -#~ msgstr "besorszámozva. Ha a 'in SZAVAK' nincs jelen akkor a \"$@\"" - -#~ msgid "is assumed. The PS3 prompt is then displayed and a line read" -#~ msgstr "használja. A PS3 prompt jelenik meg és szabvány bemenetrõl" - -#~ msgid "from the standard input. If the line consists of the number" -#~ msgstr "olvas. Ha a sorban található valamelyik szám megfelel" - -#~ msgid "corresponding to one of the displayed words, then NAME is set" -#~ msgstr "a megjelenített szavak egyikével akkor azt behelyettesíti a NÉV-be" - -#~ msgid "to that word. If the line is empty, WORDS and the prompt are" -#~ msgstr "Ha a sor üres akkor a SZAVAKAT és a promptot újra megjeleníti" - -#~ msgid "redisplayed. If EOF is read, the command completes. Any other" -#~ msgstr "" -#~ "Ha EOF jelet olvas be akkor a parancsot befejezi. Minden egyéb helyzetben" - -#~ msgid "value read causes NAME to be set to null. The line read is saved" -#~ msgstr "a NÉV értékét nullára állítja be. A beolvasott sort eltárolja a " - -#~ msgid "in the variable REPLY. COMMANDS are executed after each selection" -#~ msgstr "" -#~ "REPLY változóba. A parancsot az mindig lefuttatja egészen addig amíg" - -#~ msgid "until a break or return command is executed." -#~ msgstr "meg nem szakítjuk vagy vissza nem tér a parancs." - -#~ msgid "`|' is used to separate multiple patterns." -#~ msgstr "'|' használhatjuk több minta elválasztásához." - -#~ msgid "" -#~ "The if COMMANDS are executed. If the exit status is zero, then the then" -#~ msgstr "" -#~ "Az if PARANCS lefuttatása. Ha a kimeneti status nulla akkor a 'then'" - -#~ msgid "" -#~ "COMMANDS are executed. Otherwise, each of the elif COMMANDS are executed" -#~ msgstr " PARANCS-ot lefuttatja. Egyébként az 'elif' PARANCSAIt futtatja le" - -#~ msgid "" -#~ "in turn, and if the exit status is zero, the corresponding then COMMANDS" -#~ msgstr "" -#~ "és ha ennek a visszatérési értéke nulla akkor a megfelelõ PARANCSok " -#~ "lesznek" - -#~ msgid "" -#~ "are executed and the if command completes. Otherwise, the else COMMANDS" -#~ msgstr "" -#~ "lefuttatva és ezzel behelyezõdik az 'if' parancs. Egyébként az 'else' ág " -#~ "PARANCSAI" - -#~ msgid "" -#~ "are executed, if present. The exit status is the exit status of the last" -#~ msgstr "futnak le (persze ha jelen van). A visszatérési státusz az utolsó" - -#~ msgid "command executed, or zero if no condition tested true." -#~ msgstr "lefutott parancsé vagy nulla ha az 'if' feltétel nem igaz." - -#~ msgid "`while' COMMANDS has an exit status of zero." -#~ msgstr "'while'-ban nem tér vissza nulla kilépési státusszal." - -#~ msgid "`until' COMMANDS has an exit status which is not zero." -#~ msgstr "'until'-ban nem tér vissza nulla kilépési státusszal." - -#~ msgid "Create a simple command invoked by NAME which runs COMMANDS." -#~ msgstr "" -#~ "Létrehoz egy egyszerû parancsot amit a NÉVvel lehet meghívni és ami " -#~ "lefuttatja" - -#~ msgid "Arguments on the command line along with NAME are passed to the" -#~ msgstr "a PARANCSOKAT. A parancssorban a NÉV opciói a $0..$N" - -#~ msgid "function as $0 .. $n." -#~ msgstr "változókban jelenik meg." - -#~ msgid "entire set of commands." -#~ msgstr "átirányítás a parancsok halmazának." - -#~ msgid "This is similar to the `fg' command. Resume a stopped or background" -#~ msgstr "" -#~ "Ez hasonlít az 'fg' parancsra. Folyattatja a megállított vagy háttérbe " -#~ "rakott" - -#~ msgid "job. If you specifiy DIGITS, then that job is used. If you specify" -#~ msgstr "" -#~ "munkafolyamatokat. Ha meghatározza a SZÁMOKAT akkor azt munkafolyamatot" - -#~ msgid "" -#~ "WORD, then the job whose name begins with WORD is used. Following the" -#~ msgstr "" -#~ "használja. Ha a SZÓT adja meg akkor azokat a folyamatokat használja ami a " - -#~ msgid "job specification with a `&' places the job in the background." -#~ msgstr "" -#~ "a SZÓval kezdõdik.A munkafolyamat meghatározása egy '&'-re végzõdik akkor " -#~ "a háttérbe helyezi." - -#~ msgid "BASH_VERSION The version numbers of this Bash." -#~ msgstr "BASH_VERSION A Bash verzió száma." - -#~ msgid "CDPATH A colon separated list of directories to search" -#~ msgstr "" -#~ "CDPATH Kettõsponttal elválasztott lista azoknak a könyvtáraknak" - -#~ msgid "\t\twhen the argument to `cd' is not found in the current" -#~ msgstr "\t\thelyérõl amiket a 'cd' parancs nem talál meg az aktuális " - -#~ msgid "\t\tdirectory." -#~ msgstr "\t\tkönyvtárban." - -#~ msgid "" -#~ "HISTFILE The name of the file where your command history is stored." -#~ msgstr "" -#~ "HISTFILE A fájl neve ahol a saját parancs elõzményeket tárolja." - -#~ msgid "HISTFILESIZE The maximum number of lines this file can contain." -#~ msgstr "HISTFILESIZE Maximális sorok száma amit ez a fájl tartalmazhat." - -#~ msgid "HISTSIZE The maximum number of history lines that a running" -#~ msgstr "HISTSIZE A maximális sorok száma amit a futó" - -#~ msgid "\t\tshell can access." -#~ msgstr "\t\tparancsértelmezõ még elérhet." - -#~ msgid "HOME The complete pathname to your login directory." -#~ msgstr "HOME A teljes útvonala a saját belépési könyvtárnak." - -#~ msgid "" -#~ "HOSTTYPE The type of CPU this version of Bash is running under." -#~ msgstr "HOSTTYPE A CPU típusa annak a gépeknek ahol a Bash fut." - -#~ msgid "" -#~ "IGNOREEOF Controls the action of the shell on receipt of an EOF" -#~ msgstr "" -#~ "IGNOREEOF Ellenõrzi a parancsértelemezõ viselkedését amikor egy EOF" - -#~ msgid "\t\tcharacter as the sole input. If set, then the value" -#~ msgstr "" -#~ "\t\tkaraktert kap mint önálló bemenet. Ha be van állítva akkor ez az " - -#~ msgid "\t\tof it is the number of EOF characters that can be seen" -#~ msgstr "\t\t érték akkor azt jelenti, hogy hány üres" - -#~ msgid "\t\tin a row on an empty line before the shell will exit" -#~ msgstr "\t\tsor keljen az EOF jel elõtt, hogy kilépjen(alapból 10). Amikor" - -#~ msgid "\t\t(default 10). When unset, EOF signifies the end of input." -#~ msgstr "\t\t nincs beállítva akkor az EOF jelre befejezi a bevitelt" - -#~ msgid "MAILCHECK\tHow often, in seconds, Bash checks for new mail." -#~ msgstr "MAILCHECK\tHány másodpercenként ellenõrizze a Bash az új leveleket." - -#~ msgid "MAILPATH\tA colon-separated list of filenames which Bash checks" -#~ msgstr "MAILPATH\tEgy kettõsponttal elválasztott lista a fájl nevekrõl ahol" - -#~ msgid "\t\tfor new mail." -#~ msgstr "\t\ta Bash ellenõrzi az új leveleket." - -#~ msgid "OSTYPE\t\tThe version of Unix this version of Bash is running on." -#~ msgstr "OSTYPE\t\tA Unix verziója annak a gépnek ahol a Bash fut." - -#~ msgid "PATH A colon-separated list of directories to search when" -#~ msgstr "" -#~ "PATH Kettõsponttal elválasztott lista azokról a könyvtárakról" - -#~ msgid "\t\tlooking for commands." -#~ msgstr "\t\tahol a Bash keresi a parancsokat." - -#~ msgid "PROMPT_COMMAND A command to be executed before the printing of each" -#~ msgstr "PROMPT_COMMAND A parancs amit mindig lefuttat mielõtt kiírja " - -#~ msgid "\t\tprimary prompt." -#~ msgstr "\t\tmielõtt megjelenítené az elsõdleges promptot." - -#~ msgid "PS1 The primary prompt string." -#~ msgstr "PS1 Az elsõdleges prompt sztringje." - -#~ msgid "PS2 The secondary prompt string." -#~ msgstr "PS2 másodlagos prompt sztringje." - -#~ msgid "TERM The name of the current terminal type." -#~ msgstr "TERM A jelenlegi terminál típusának a neve." - -#~ msgid "auto_resume Non-null means a command word appearing on a line by" -#~ msgstr "" -#~ "auto_resume A kitöltött változó azt jelenti a kiadott parancsokat" - -#~ msgid "\t\titself is first looked for in the list of currently" -#~ msgstr "\t\telõbb megnézi a jelenleg megállított munka" - -#~ msgid "\t\tstopped jobs. If found there, that job is foregrounded." -#~ msgstr "\t\tfolyamatok között.Ha megtalálja akkor azt folytatja." - -#~ msgid "\t\tA value of `exact' means that the command word must" -#~ msgstr "\t\tAz `exact' értéke azt jelenti, hogy a parancsnak teljesen meg" - -#~ msgid "\t\texactly match a command in the list of stopped jobs. A" -#~ msgstr "\t\tkell egyezni a leállított munkafolyamat nevével." - -#~ msgid "\t\tvalue of `substring' means that the command word must" -#~ msgstr "\t\tA 'substring' érték pedig azt jelenti, hogy a parancsnak csak " - -#~ msgid "\t\tmatch a substring of the job. Any other value means that" -#~ msgstr "" -#~ "\t\tegy része is elegendõ az egyezéshez.Minden egyéb érték azt jelenti," - -#~ msgid "\t\tthe command must be a prefix of a stopped job." -#~ msgstr "" -#~ "\t\t hogy a parancs elõtéte kell hogy legyen a legállított munkafolyamat." - -#~ msgid "command_oriented_history" -#~ msgstr "command_oriented_history" - -#~ msgid "" -#~ " Non-null means to save multiple-line commands together on" -#~ msgstr "" -#~ " A változó kitöltése azt jelenti, hogy több soros " -#~ "parancsokat" - -#~ msgid " a single history line." -#~ msgstr " egy sorba rakja az elõzmények közé." - -#~ msgid "histchars Characters controlling history expansion and quick" -#~ msgstr "" -#~ "histchars Ezekkel a karakterekkel befolyásolható az elõzmények gyors" - -#~ msgid "\t\tsubstitution. The first character is the history" -#~ msgstr "\t\tbehelyettesítése. Az elõzmények elsõ karakterre" - -#~ msgid "\t\tsubstitution character, usually `!'. The second is" -#~ msgstr "\t\ta behelyettesítõ karakter általában '!'. A második" - -#~ msgid "\t\tthe `quick substitution' character, usually `^'. The" -#~ msgstr "\t\ta gyors behelyettesítést jelenti általában `^' jelenti. A" - -#~ msgid "\t\tthird is the `history comment' character, usually `#'." -#~ msgstr "\t\tharmadik a elõzmények megjegyzés karakterre általában '#'." - -#~ msgid "HISTCONTROL\tSet to a value of `ignorespace', it means don't enter" -#~ msgstr "HISTCONTROL\tA változó `ignorespace' értéke azt jelenti, hogy" - -#~ msgid "\t\tlines which begin with a space or tab on the history" -#~ msgstr "" -#~ "\t\tazokat a sorokat amelyek helyjellel vagy tabulátorral kezdõdnek " -#~ "kihagyja az" - -#~ msgid "\t\tlist. Set to a value of `ignoredups', it means don't" -#~ msgstr "\t\telõzmények közül.Az `ignoredups' jelentése" - -#~ msgid "\t\tenter lines which match the last entered line. Set to" -#~ msgstr "\t\tha a beírt sor azonos az elõzõvel akkor nem rögzíti.Ha " - -#~ msgid "\t\t`ignoreboth' means to combine the two options. Unset," -#~ msgstr "" -#~ "\t\t`ignoreboth'-ra állítjuk be akkor a két tulajdonságot ötvözi." -#~ "Leállítása" - -#~ msgid "\t\tor set to any other value than those above means to save" -#~ msgstr "\t\tvagy már ha értékre állítjuk be akkor a mentés során" - -#~ msgid "\t\tall lines on the history list." -#~ msgstr "\t\tminden sor bele kerül az elõzmények közé." - -#~ msgid "Toggle the values of variables controlling optional behavior." -#~ msgstr "A változok értékeivel szabályozhatjuk az opciók viselkedését." - -#~ msgid "The -s flag means to enable (set) each OPTNAME; the -u flag" -#~ msgstr "A '-s'-el engedélyezhetjük az összes OPTNEVEt, a '-u'-val pedig" - -#~ msgid "unsets each OPTNAME. The -q flag suppresses output; the exit" -#~ msgstr "letilthatjuk õket. A '-q' opcióval a kimenetet letilthatjuk el így" - -#~ msgid "status indicates whether each OPTNAME is set or unset. The -o" -#~ msgstr "csak a visszatérési értékek jelzik az OPTNEVEk állapotát. A '-o'" - -#~ msgid "option restricts the OPTNAMEs to those defined for use with" -#~ msgstr "opcióval rögzítheti a OPTNEVEK-et ezeket a meghatározásokat" - -#~ msgid "`set -o'. With no options, or with the -p option, a list of all" -#~ msgstr "" -#~ "a 'set -o' használhatjuk. Opciók nélkül vagy a 'p' opció kilistázza az " -#~ "összes" - -#~ msgid "settable options is displayed, with an indication of whether or" -#~ msgstr "beállítható opciót jelezve azokat amik már be vannak állítva és" - -#~ msgid "not each is set." -#~ msgstr "azokat amik nincsenek beállítva." +"Sorok olvasása egy tömbváltozóba.\n" +" \n" +" „mapfile†szinonimája." @@ -142,6 +142,8 @@ typedef struct _sh_parser_state_t { /* Nothing right now for multibyte state, but might want something later. */ #endif + char **prompt_string_pointer; + /* history state affecting or modified by the parser */ int current_command_line_count; #if defined (HISTORY) diff --git a/shell.h~ b/shell.h~ new file mode 100644 index 00000000..5eb0caa4 --- /dev/null +++ b/shell.h~ @@ -0,0 +1,167 @@ +/* shell.h -- The data structures used by the shell */ + +/* Copyright (C) 1993-2009 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "bashjmp.h" + +#include "command.h" +#include "syntax.h" +#include "general.h" +#include "error.h" +#include "variables.h" +#include "arrayfunc.h" +#include "quit.h" +#include "maxpath.h" +#include "unwind_prot.h" +#include "dispose_cmd.h" +#include "make_cmd.h" +#include "ocache.h" +#include "subst.h" +#include "sig.h" +#include "pathnames.h" +#include "externs.h" + +extern int EOF_Reached; + +#define NO_PIPE -1 +#define REDIRECT_BOTH -2 + +#define NO_VARIABLE -1 + +/* Values that can be returned by execute_command (). */ +#define EXECUTION_FAILURE 1 +#define EXECUTION_SUCCESS 0 + +/* Usage messages by builtins result in a return status of 2. */ +#define EX_BADUSAGE 2 + +/* Special exit statuses used by the shell, internally and externally. */ +#define EX_RETRYFAIL 124 +#define EX_WEXPCOMSUB 125 +#define EX_BINARY_FILE 126 +#define EX_NOEXEC 126 +#define EX_NOINPUT 126 +#define EX_NOTFOUND 127 + +#define EX_SHERRBASE 256 /* all special error values are > this. */ + +#define EX_BADSYNTAX 257 /* shell syntax error */ +#define EX_USAGE 258 /* syntax error in usage */ +#define EX_REDIRFAIL 259 /* redirection failed */ +#define EX_BADASSIGN 260 /* variable assignment error */ +#define EX_EXPFAIL 261 /* word expansion failed */ + +/* Flag values that control parameter pattern substitution. */ +#define MATCH_ANY 0x000 +#define MATCH_BEG 0x001 +#define MATCH_END 0x002 + +#define MATCH_TYPEMASK 0x003 + +#define MATCH_GLOBREP 0x010 +#define MATCH_QUOTED 0x020 +#define MATCH_STARSUB 0x040 + +/* Some needed external declarations. */ +extern char **shell_environment; +extern WORD_LIST *rest_of_args; + +/* Generalized global variables. */ +extern int debugging_mode; +extern int executing, login_shell; +extern int interactive, interactive_shell; +extern int startup_state; +extern int subshell_environment; +extern int shell_compatibility_level; + +/* Structure to pass around that holds a bitmap of file descriptors + to close, and the size of that structure. Used in execute_cmd.c. */ +struct fd_bitmap { + int size; + char *bitmap; +}; + +#define FD_BITMAP_SIZE 32 + +#define CTLESC '\001' +#define CTLNUL '\177' + +/* Information about the current user. */ +struct user_info { + uid_t uid, euid; + gid_t gid, egid; + char *user_name; + char *shell; /* shell from the password file */ + char *home_dir; +}; + +extern struct user_info current_user; + +/* Force gcc to not clobber X on a longjmp(). Old versions of gcc mangle + this badly. */ +#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 8) +# define USE_VAR(x) ((void) &(x)) +#else +# define USE_VAR(x) +#endif + +/* Structure in which to save partial parsing state when doing things like + PROMPT_COMMAND and bash_execute_unix_command execution. */ + +typedef struct _sh_parser_state_t { + + /* parsing state */ + int parser_state; + int *token_state; + + /* input line state -- line number saved elsewhere */ + int input_line_terminator; + int eof_encountered; + +#if defined (HANDLE_MULTIBYTE) + /* Nothing right now for multibyte state, but might want something later. */ +#endif + + /* history state affecting or modified by the parser */ + int current_command_line_count; +#if defined (HISTORY) + int remember_on_history; + int history_expansion_inhibited; +#endif + + /* execution state possibly modified by the parser */ + int last_command_exit_value; +#if defined (ARRAY_VARS) + ARRAY *pipestatus; +#endif + sh_builtin_func_t *last_shell_builtin, *this_shell_builtin; + + /* flags state affecting the parser */ + int expand_aliases; + int echo_input_at_read; + +} sh_parser_state_t; + +/* Let's try declaring these here. */ +extern sh_parser_state_t *save_parser_state __P((sh_parser_state_t *)); +extern void restore_parser_state __P((sh_parser_state_t *)); @@ -245,10 +245,8 @@ static wchar_t *remove_wpattern __P((wchar_t *, size_t, wchar_t *, int)); #endif static char *remove_pattern __P((char *, char *, int)); -static int match_pattern_char __P((char *, char *)); static int match_upattern __P((char *, char *, int, char **, char **)); #if defined (HANDLE_MULTIBYTE) -static int match_pattern_wchar __P((wchar_t *, wchar_t *)); static int match_wpattern __P((wchar_t *, char **, size_t, wchar_t *, int, char **, char **)); #endif static int match_pattern __P((char *, char *, int, char **, char **)); @@ -260,7 +258,7 @@ static char *parameter_list_remove_pattern __P((int, char *, int, int)); #ifdef ARRAY_VARS static char *array_remove_pattern __P((SHELL_VAR *, char *, int, char *, int)); #endif -static char *parameter_brace_remove_pattern __P((char *, char *, char *, int, int)); +static char *parameter_brace_remove_pattern __P((char *, char *, int, char *, int, int, int)); static char *process_substitute __P((char *, int)); @@ -274,7 +272,7 @@ static int valid_brace_expansion_word __P((char *, int)); static int chk_atstar __P((char *, int, int *, int *)); static int chk_arithsub __P((const char *, int)); -static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int)); +static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int, arrayind_t *)); static WORD_DESC *parameter_brace_expand_indir __P((char *, int, int, int *, int *)); static WORD_DESC *parameter_brace_expand_rhs __P((char *, char *, int, int, int *, int *)); static void parameter_brace_expand_error __P((char *, char *)); @@ -284,18 +282,18 @@ static intmax_t parameter_brace_expand_length __P((char *)); static char *skiparith __P((char *, int)); static int verify_substring_values __P((SHELL_VAR *, char *, char *, int, intmax_t *, intmax_t *)); -static int get_var_and_type __P((char *, char *, int, SHELL_VAR **, char **)); +static int get_var_and_type __P((char *, char *, arrayind_t, int, int, SHELL_VAR **, char **)); static char *mb_substring __P((char *, int, int)); -static char *parameter_brace_substring __P((char *, char *, char *, int)); +static char *parameter_brace_substring __P((char *, char *, int, char *, int, int)); static int shouldexp_replacement __P((char *)); static char *pos_params_pat_subst __P((char *, char *, char *, int)); -static char *parameter_brace_patsub __P((char *, char *, char *, int)); +static char *parameter_brace_patsub __P((char *, char *, int, char *, int, int)); static char *pos_params_casemod __P((char *, char *, int, int)); -static char *parameter_brace_casemod __P((char *, char *, int, char *, int)); +static char *parameter_brace_casemod __P((char *, char *, int, int, char *, int, int)); static WORD_DESC *parameter_brace_expand __P((char *, int *, int, int, int *, int *)); static WORD_DESC *param_expand __P((char *, int *, int, int *, int *, int *, int *, int)); @@ -611,7 +609,6 @@ unquoted_substring (substr, string) { case '\\': sindex++; - if (string[sindex]) ADVANCE_CHAR (string, slen, sindex); break; @@ -1695,7 +1692,7 @@ skip_to_delim (string, start, delims, flags) { int i, pass_next, backq, si, c, invert, skipquote, skipcmd; size_t slen; - char *temp; + char *temp, open[3]; DECLARE_MBSTATE; slen = strlen (string + start) + start; @@ -1778,6 +1775,25 @@ skip_to_delim (string, start, delims, flags) continue; } #endif /* PROCESS_SUBSTITUTION */ +#if defined (EXTENDED_GLOB) + else if ((flags & SD_EXTGLOB) && extended_glob && string[i+1] == LPAREN && member (c, "?*+!@")) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + + open[0] = c; + open[1] = LPAREN; + open[2] = '\0'; + temp = extract_delimited_string (string, &si, open, "(", ")", SX_NOALLOC); /* ) */ + + i = si; + if (string[i] == '\0') /* don't increment i past EOS in loop */ + break; + i++; + continue; + } +#endif else if ((skipquote || invert) && (member (c, delims) == 0)) break; else @@ -3972,36 +3988,6 @@ remove_pattern (param, pattern, op) } } -/* Return 1 of the first character of STRING could match the first - character of pattern PAT. Used to avoid n2 calls to strmatch(). */ -static int -match_pattern_char (pat, string) - char *pat, *string; -{ - char c; - - if (*string == 0) - return (0); - - switch (c = *pat++) - { - default: - return (*string == c); - case '\\': - return (*string == *pat); - case '?': - return (*pat == LPAREN ? 1 : (*string != '\0')); - case '*': - return (1); - case '+': - case '!': - case '@': - return (*pat == LPAREN ? 1 : (*string == c)); - case '[': - return (*string != '\0'); - } -} - /* Match PAT anywhere in STRING and return the match boundaries. This returns 1 in case of a successful match, 0 otherwise. SP and EP are pointers into the string where the match begins and @@ -4014,7 +4000,7 @@ match_upattern (string, pat, mtype, sp, ep) int mtype; char **sp, **ep; { - int c, len; + int c, len, mlen; register char *p, *p1, *npat; char *end; @@ -4050,6 +4036,8 @@ match_upattern (string, pat, mtype, sp, ep) len = STRLEN (string); end = string + len; + mlen = umatchlen (pat, len); + switch (mtype) { case MATCH_ANY: @@ -4057,7 +4045,15 @@ match_upattern (string, pat, mtype, sp, ep) { if (match_pattern_char (pat, p)) { +#if 0 for (p1 = end; p1 >= p; p1--) +#else + p1 = (mlen == -1) ? end : p + mlen; + /* extra -1 to handle case of p1 == end */ + if (p1 - p + mlen - 1 > len) + break; + for ( ; p1 >= p; p1--) +#endif { c = *p1; *p1 = '\0'; if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) @@ -4068,6 +4064,11 @@ match_upattern (string, pat, mtype, sp, ep) return 1; } *p1 = c; +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } } } @@ -4078,7 +4079,11 @@ match_upattern (string, pat, mtype, sp, ep) if (match_pattern_char (pat, string) == 0) return (0); +#if 0 for (p = end; p >= string; p--) +#else + for (p = (mlen == -1) ? end : string + mlen; p >= string; p--) +#endif { c = *p; *p = '\0'; if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0) @@ -4089,12 +4094,21 @@ match_upattern (string, pat, mtype, sp, ep) return 1; } *p = c; +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } return (0); case MATCH_END: +#if 0 for (p = string; p <= end; p++) +#else + for (p = end - ((mlen == -1) ? len : mlen); p <= end; p++) +#endif { if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) { @@ -4102,7 +4116,11 @@ match_upattern (string, pat, mtype, sp, ep) *ep = end; return 1; } - +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } return (0); @@ -4112,36 +4130,6 @@ match_upattern (string, pat, mtype, sp, ep) } #if defined (HANDLE_MULTIBYTE) -/* Return 1 of the first character of WSTRING could match the first - character of pattern WPAT. Wide character version. */ -static int -match_pattern_wchar (wpat, wstring) - wchar_t *wpat, *wstring; -{ - wchar_t wc; - - if (*wstring == 0) - return (0); - - switch (wc = *wpat++) - { - default: - return (*wstring == wc); - case L'\\': - return (*wstring == *wpat); - case L'?': - return (*wpat == LPAREN ? 1 : (*wstring != L'\0')); - case L'*': - return (1); - case L'+': - case L'!': - case L'@': - return (*wpat == LPAREN ? 1 : (*wstring == wc)); - case L'[': - return (*wstring != L'\0'); - } -} - /* Match WPAT anywhere in WSTRING and return the match boundaries. This returns 1 in case of a successful match, 0 otherwise. Wide character version. */ @@ -4155,12 +4143,9 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) char **sp, **ep; { wchar_t wc, *wp, *nwpat, *wp1; - int len; -#if 0 - size_t n, n1; /* Apple's gcc seems to miscompile this badly */ -#else + size_t len; + int mlen; int n, n1; -#endif /* If the pattern doesn't match anywhere in the string, go ahead and short-circuit right away. A minor optimization, saves a bunch of @@ -4168,8 +4153,6 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) characters) if the match is unsuccessful. To preserve the semantics of the substring matches below, we make sure that the pattern has `*' as first and last character, making a new pattern if necessary. */ - /* XXX - check this later if I ever implement `**' with special meaning, - since this will potentially result in `**' at the beginning or end */ len = wcslen (wpat); if (wpat[0] != L'*' || (wpat[0] == L'*' && wpat[1] == WLPAREN && extended_glob) || wpat[len - 1] != L'*') { @@ -4191,6 +4174,8 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) if (len == FNM_NOMATCH) return (0); + mlen = wmatchlen (wpat, wstrlen); +/* itrace("wmatchlen (%ls) -> %d", wpat, mlen); */ switch (mtype) { case MATCH_ANY: @@ -4198,7 +4183,15 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) { if (match_pattern_wchar (wpat, wstring + n)) { +#if 0 for (n1 = wstrlen; n1 >= n; n1--) +#else + n1 = (mlen == -1) ? wstrlen : n + mlen; + if (n1 > wstrlen) + break; + + for ( ; n1 >= n; n1--) +#endif { wc = wstring[n1]; wstring[n1] = L'\0'; if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) @@ -4209,6 +4202,11 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) return 1; } wstring[n1] = wc; +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } } } @@ -4219,7 +4217,11 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) if (match_pattern_wchar (wpat, wstring) == 0) return (0); +#if 0 for (n = wstrlen; n >= 0; n--) +#else + for (n = (mlen == -1) ? wstrlen : mlen; n >= 0; n--) +#endif { wc = wstring[n]; wstring[n] = L'\0'; if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG) == 0) @@ -4230,12 +4232,21 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) return 1; } wstring[n] = wc; +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } return (0); case MATCH_END: +#if 0 for (n = 0; n <= wstrlen; n++) +#else + for (n = wstrlen - ((mlen == -1) ? wstrlen : mlen); n <= wstrlen; n++) +#endif { if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) { @@ -4243,6 +4254,11 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) *ep = indices[wstrlen]; return 1; } +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } return (0); @@ -4446,9 +4462,11 @@ array_remove_pattern (var, pattern, patspec, varname, quoted) #endif /* ARRAY_VARS */ static char * -parameter_brace_remove_pattern (varname, value, patstr, rtype, quoted) - char *varname, *value, *patstr; - int rtype, quoted; +parameter_brace_remove_pattern (varname, value, ind, patstr, rtype, quoted, flags) + char *varname, *value; + int ind; + char *patstr; + int rtype, quoted, flags; { int vtype, patspec, starsub; char *temp1, *val, *pattern; @@ -4459,7 +4477,7 @@ parameter_brace_remove_pattern (varname, value, patstr, rtype, quoted) this_command_name = varname; - vtype = get_var_and_type (varname, value, quoted, &v, &val); + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); if (vtype == -1) return ((char *)NULL); @@ -5571,20 +5589,25 @@ chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at) the shell, e.g., "@", "$", "*", etc. QUOTED, if non-zero, means that NAME was found inside of a double-quoted expression. */ static WORD_DESC * -parameter_brace_expand_word (name, var_is_special, quoted, pflags) +parameter_brace_expand_word (name, var_is_special, quoted, pflags, indp) char *name; int var_is_special, quoted, pflags; + arrayind_t *indp; { WORD_DESC *ret; char *temp, *tt; intmax_t arg_index; SHELL_VAR *var; int atype, rflags; + arrayind_t ind; ret = 0; temp = 0; rflags = 0; + if (indp) + *indp = INTMAX_MIN; + /* Handle multiple digit arguments, as in ${11}. */ if (legal_number (name, &arg_index)) { @@ -5611,11 +5634,16 @@ parameter_brace_expand_word (name, var_is_special, quoted, pflags) #if defined (ARRAY_VARS) else if (valid_array_reference (name)) { - temp = array_value (name, quoted, &atype, (arrayind_t *)NULL); + temp = array_value (name, quoted, 0, &atype, &ind); if (atype == 0 && temp) - temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) - ? quote_string (temp) - : quote_escapes (temp); + { + temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (temp) + : quote_escapes (temp); + rflags |= W_ARRAYIND; + if (indp) + *indp = ind; + } else if (atype == 1 && temp && QUOTED_NULL (temp) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) rflags |= W_HASQUOTEDNULL; } @@ -5666,7 +5694,7 @@ parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, c char *temp, *t; WORD_DESC *w; - w = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND); + w = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND, 0); t = w->word; /* Have to dequote here if necessary */ if (t) @@ -5683,7 +5711,7 @@ parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, c if (t == 0) return (WORD_DESC *)NULL; - w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0); + w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0, 0); free (t); return w; @@ -6119,13 +6147,18 @@ verify_substring_values (v, value, substr, vtype, e1p, e2p) /* Return the type of variable specified by VARNAME (simple variable, positional param, or array variable). Also return the value specified by VARNAME (value of a variable or a reference to an array element). + QUOTED is the standard description of quoting state, using Q_* defines. + FLAGS is currently a set of flags to pass to array_value. If IND is + non-null and not INTMAX_MIN, and FLAGS includes AV_USEIND, IND is + passed to array_value so the array index is not computed again. If this returns VT_VARIABLE, the caller assumes that CTLESC and CTLNUL characters in the value are quoted with CTLESC and takes appropriate steps. For convenience, *VALP is set to the dequoted VALUE. */ static int -get_var_and_type (varname, value, quoted, varp, valp) +get_var_and_type (varname, value, ind, quoted, flags, varp, valp) char *varname, *value; - int quoted; + arrayind_t ind; + int quoted, flags; SHELL_VAR **varp; char **valp; { @@ -6134,6 +6167,7 @@ get_var_and_type (varname, value, quoted, varp, valp) #if defined (ARRAY_VARS) SHELL_VAR *v; #endif + arrayind_t lind; /* This sets vtype to VT_VARIABLE or VT_POSPARMS */ vtype = (varname[0] == '@' || varname[0] == '*') && varname[1] == '\0'; @@ -6145,6 +6179,9 @@ get_var_and_type (varname, value, quoted, varp, valp) if (valid_array_reference (varname)) { v = array_variable_part (varname, &temp, (int *)0); + /* If we want to signal array_value to use an already-computed index, + set LIND to that index */ + lind = (ind != INTMAX_MIN && (flags & AV_USEIND)) ? ind : 0; if (v && (array_p (v) || assoc_p (v))) { /* [ */ if (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']') @@ -6158,7 +6195,7 @@ get_var_and_type (varname, value, quoted, varp, valp) else { vtype = VT_ARRAYMEMBER; - *valp = array_value (varname, 1, (int *)NULL, (arrayind_t *)NULL); + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); } *varp = v; } @@ -6175,7 +6212,7 @@ get_var_and_type (varname, value, quoted, varp, valp) { vtype = VT_ARRAYMEMBER; *varp = v; - *valp = array_value (varname, 1, (int *)NULL, (arrayind_t *)NULL); + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); } } else if ((v = find_variable (varname)) && (invisible_p (v) == 0) && (assoc_p (v) || array_p (v))) @@ -6242,9 +6279,11 @@ mb_substring (string, s, e) VARNAME. If VARNAME is an array variable, use the array elements. */ static char * -parameter_brace_substring (varname, value, substr, quoted) - char *varname, *value, *substr; - int quoted; +parameter_brace_substring (varname, value, ind, substr, quoted, flags) + char *varname, *value; + int ind; + char *substr; + int quoted, flags; { intmax_t e1, e2; int vtype, r, starsub; @@ -6257,7 +6296,7 @@ parameter_brace_substring (varname, value, substr, quoted) oname = this_command_name; this_command_name = varname; - vtype = get_var_and_type (varname, value, quoted, &v, &val); + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); if (vtype == -1) { this_command_name = oname; @@ -6353,6 +6392,9 @@ pat_subst (string, pat, rep, mflags) char *ret, *s, *e, *str, *rstr, *mstr; int rsize, rptr, l, replen, mtype, rxpand, rslen, mlen; + if (string == 0) + return (savestring ("")); + mtype = mflags & MATCH_TYPEMASK; #if 0 /* bash-4.2 ? */ @@ -6371,7 +6413,7 @@ pat_subst (string, pat, rep, mflags) if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END)) { replen = STRLEN (rep); - l = strlen (string); + l = STRLEN (string); ret = (char *)xmalloc (replen + l + 2); if (replen == 0) strcpy (ret, string); @@ -6449,7 +6491,7 @@ pat_subst (string, pat, rep, mflags) } /* Now copy the unmatched portion of the input string */ - if (*str) + if (str && *str) { RESIZE_MALLOCED_BUFFER (ret, rptr, STRLEN(str) + 1, rsize, 64); strcpy (ret + rptr, str); @@ -6510,9 +6552,11 @@ pos_params_pat_subst (string, pat, rep, mflags) and the string to substitute. QUOTED is a flags word containing the type of quoting currently in effect. */ static char * -parameter_brace_patsub (varname, value, patsub, quoted) - char *varname, *value, *patsub; - int quoted; +parameter_brace_patsub (varname, value, ind, patsub, quoted, flags) + char *varname, *value; + int ind; + char *patsub; + int quoted, flags; { int vtype, mflags, starsub, delim; char *val, *temp, *pat, *rep, *p, *lpatsub, *tt; @@ -6523,7 +6567,7 @@ parameter_brace_patsub (varname, value, patsub, quoted) this_command_name = varname; - vtype = get_var_and_type (varname, value, quoted, &v, &val); + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); if (vtype == -1) return ((char *)NULL); @@ -6698,11 +6742,11 @@ pos_params_modcase (string, pat, modop, mflags) to perform. QUOTED is a flags word containing the type of quoting currently in effect. */ static char * -parameter_brace_casemod (varname, value, modspec, patspec, quoted) +parameter_brace_casemod (varname, value, ind, modspec, patspec, quoted, flags) char *varname, *value; - int modspec; + int ind, modspec; char *patspec; - int quoted; + int quoted, flags; { int vtype, starsub, modop, mflags, x; char *val, *temp, *pat, *p, *lpat, *tt; @@ -6713,7 +6757,7 @@ parameter_brace_casemod (varname, value, modspec, patspec, quoted) this_command_name = varname; - vtype = get_var_and_type (varname, value, quoted, &v, &val); + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); if (vtype == -1) return ((char *)NULL); @@ -6864,6 +6908,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta WORD_DESC *tdesc, *ret; int t_index, sindex, c, tflag, modspec; intmax_t number; + arrayind_t ind; temp = temp1 = value = (char *)NULL; var_is_set = var_is_null = var_is_special = check_nullness = 0; @@ -6890,6 +6935,8 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta ret = 0; tflag = 0; + ind = INTMAX_MIN; + /* If the name really consists of a special variable, then make sure that we have the entire name. We don't allow indirect references to special variables except `#', `?', `@' and `*'. */ @@ -7096,7 +7143,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta if (want_indir) tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at); else - tdesc = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND|(pflags&PF_NOSPLIT2)); + tdesc = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND|(pflags&PF_NOSPLIT2), &ind); if (tdesc) { @@ -7134,7 +7181,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta /* If this is a substring spec, process it and add the result. */ if (want_substring) { - temp1 = parameter_brace_substring (name, temp, value, quoted); + temp1 = parameter_brace_substring (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); FREE (name); FREE (value); FREE (temp); @@ -7152,7 +7199,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta } else if (want_patsub) { - temp1 = parameter_brace_patsub (name, temp, value, quoted); + temp1 = parameter_brace_patsub (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); FREE (name); FREE (value); FREE (temp); @@ -7173,7 +7220,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta #if defined (CASEMOD_EXPANSIONS) else if (want_casemod) { - temp1 = parameter_brace_casemod (name, temp, modspec, value, quoted); + temp1 = parameter_brace_casemod (name, temp, ind, modspec, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); FREE (name); FREE (value); FREE (temp); @@ -7222,7 +7269,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta FREE (value); break; } - temp1 = parameter_brace_remove_pattern (name, temp, value, c, quoted); + temp1 = parameter_brace_remove_pattern (name, temp, ind, value, c, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); free (temp); free (value); free (name); @@ -8286,10 +8333,12 @@ add_twochars: else temp = (char *)NULL; +#if 0 /* We do not want to add quoted nulls to strings that are only partially quoted; we can throw them away. */ if (temp == 0 && quoted_state == PARTIALLY_QUOTED) continue; +#endif add_quoted_string: diff --git a/subst.c.new b/subst.c.new new file mode 100644 index 00000000..51c32461 --- /dev/null +++ b/subst.c.new @@ -0,0 +1,9407 @@ +/* subst.c -- The part of the shell that does parameter, command, arithmetic, + and globbing substitutions. */ + +/* ``Have a little faith, there's magic in the night. You ain't a + beauty, but, hey, you're alright.'' */ + +/* Copyright (C) 1987-2010 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" +#include <stdio.h> +#include "chartypes.h" +#if defined (HAVE_PWD_H) +# include <pwd.h> +#endif +#include <signal.h> +#include <errno.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" +#include "posixstat.h" +#include "bashintl.h" + +#include "shell.h" +#include "parser.h" +#include "flags.h" +#include "jobs.h" +#include "execute_cmd.h" +#include "filecntl.h" +#include "trap.h" +#include "pathexp.h" +#include "mailcheck.h" + +#include "shmbutil.h" + +#include "builtins/getopt.h" +#include "builtins/common.h" + +#include "builtins/builtext.h" + +#include <tilde/tilde.h> +#include <glob/strmatch.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* The size that strings change by. */ +#define DEFAULT_INITIAL_ARRAY_SIZE 112 +#define DEFAULT_ARRAY_SIZE 128 + +/* Variable types. */ +#define VT_VARIABLE 0 +#define VT_POSPARMS 1 +#define VT_ARRAYVAR 2 +#define VT_ARRAYMEMBER 3 +#define VT_ASSOCVAR 4 + +#define VT_STARSUB 128 /* $* or ${array[*]} -- used to split */ + +/* Flags for quoted_strchr */ +#define ST_BACKSL 0x01 +#define ST_CTLESC 0x02 +#define ST_SQUOTE 0x04 /* unused yet */ +#define ST_DQUOTE 0x08 /* unused yet */ + +/* Flags for the `pflags' argument to param_expand() */ +#define PF_NOCOMSUB 0x01 /* Do not perform command substitution */ +#define PF_IGNUNBOUND 0x02 /* ignore unbound vars even if -u set */ +#define PF_NOSPLIT2 0x04 /* same as W_NOSPLIT2 */ + +/* These defs make it easier to use the editor. */ +#define LBRACE '{' +#define RBRACE '}' +#define LPAREN '(' +#define RPAREN ')' + +#if defined (HANDLE_MULTIBYTE) +#define WLPAREN L'(' +#define WRPAREN L')' +#endif + +/* Evaluates to 1 if C is one of the shell's special parameters whose length + can be taken, but is also one of the special expansion characters. */ +#define VALID_SPECIAL_LENGTH_PARAM(c) \ + ((c) == '-' || (c) == '?' || (c) == '#') + +/* Evaluates to 1 if C is one of the shell's special parameters for which an + indirect variable reference may be made. */ +#define VALID_INDIR_PARAM(c) \ + ((c) == '#' || (c) == '?' || (c) == '@' || (c) == '*') + +/* Evaluates to 1 if C is one of the OP characters that follows the parameter + in ${parameter[:]OPword}. */ +#define VALID_PARAM_EXPAND_CHAR(c) (sh_syntaxtab[(unsigned char)c] & CSUBSTOP) + +/* Evaluates to 1 if this is one of the shell's special variables. */ +#define SPECIAL_VAR(name, wi) \ + ((DIGIT (*name) && all_digits (name)) || \ + (name[1] == '\0' && (sh_syntaxtab[(unsigned char)*name] & CSPECVAR)) || \ + (wi && name[2] == '\0' && VALID_INDIR_PARAM (name[1]))) + +/* An expansion function that takes a string and a quoted flag and returns + a WORD_LIST *. Used as the type of the third argument to + expand_string_if_necessary(). */ +typedef WORD_LIST *EXPFUNC __P((char *, int)); + +/* Process ID of the last command executed within command substitution. */ +pid_t last_command_subst_pid = NO_PID; +pid_t current_command_subst_pid = NO_PID; + +/* Variables used to keep track of the characters in IFS. */ +SHELL_VAR *ifs_var; +char *ifs_value; +unsigned char ifs_cmap[UCHAR_MAX + 1]; + +#if defined (HANDLE_MULTIBYTE) +unsigned char ifs_firstc[MB_LEN_MAX]; +size_t ifs_firstc_len; +#else +unsigned char ifs_firstc; +#endif + +/* Sentinel to tell when we are performing variable assignments preceding a + command name and putting them into the environment. Used to make sure + we use the temporary environment when looking up variable values. */ +int assigning_in_environment; + +/* Used to hold a list of variable assignments preceding a command. Global + so the SIGCHLD handler in jobs.c can unwind-protect it when it runs a + SIGCHLD trap and so it can be saved and restored by the trap handlers. */ +WORD_LIST *subst_assign_varlist = (WORD_LIST *)NULL; + +/* Extern functions and variables from different files. */ +extern int last_command_exit_value, last_command_exit_signal; +extern int subshell_environment, line_number; +extern int subshell_level, parse_and_execute_level, sourcelevel; +extern int eof_encountered; +extern int return_catch_flag, return_catch_value; +extern pid_t dollar_dollar_pid; +extern int posixly_correct; +extern char *this_command_name; +extern struct fd_bitmap *current_fds_to_close; +extern int wordexp_only; +extern int expanding_redir; +extern int tempenv_assign_error; + +#if !defined (HAVE_WCSDUP) && defined (HANDLE_MULTIBYTE) +extern wchar_t *wcsdup __P((const wchar_t *)); +#endif + +/* Non-zero means to allow unmatched globbed filenames to expand to + a null file. */ +int allow_null_glob_expansion; + +/* Non-zero means to throw an error when globbing fails to match anything. */ +int fail_glob_expansion; + +#if 0 +/* Variables to keep track of which words in an expanded word list (the + output of expand_word_list_internal) are the result of globbing + expansions. GLOB_ARGV_FLAGS is used by execute_cmd.c. + (CURRENTLY UNUSED). */ +char *glob_argv_flags; +static int glob_argv_flags_size; +#endif + +static WORD_LIST expand_word_error, expand_word_fatal; +static WORD_DESC expand_wdesc_error, expand_wdesc_fatal; +static char expand_param_error, expand_param_fatal; +static char extract_string_error, extract_string_fatal; + +/* Tell the expansion functions to not longjmp back to top_level on fatal + errors. Enabled when doing completion and prompt string expansion. */ +static int no_longjmp_on_fatal_error = 0; + +/* Set by expand_word_unsplit; used to inhibit splitting and re-joining + $* on $IFS, primarily when doing assignment statements. */ +static int expand_no_split_dollar_star = 0; + +/* A WORD_LIST of words to be expanded by expand_word_list_internal, + without any leading variable assignments. */ +static WORD_LIST *garglist = (WORD_LIST *)NULL; + +static char *quoted_substring __P((char *, int, int)); +static int quoted_strlen __P((char *)); +static char *quoted_strchr __P((char *, int, int)); + +static char *expand_string_if_necessary __P((char *, int, EXPFUNC *)); +static inline char *expand_string_to_string_internal __P((char *, int, EXPFUNC *)); +static WORD_LIST *call_expand_word_internal __P((WORD_DESC *, int, int, int *, int *)); +static WORD_LIST *expand_string_internal __P((char *, int)); +static WORD_LIST *expand_string_leave_quoted __P((char *, int)); +static WORD_LIST *expand_string_for_rhs __P((char *, int, int *, int *)); + +static WORD_LIST *list_quote_escapes __P((WORD_LIST *)); +static char *make_quoted_char __P((int)); +static WORD_LIST *quote_list __P((WORD_LIST *)); + +static int unquoted_substring __P((char *, char *)); +static int unquoted_member __P((int, char *)); + +#if defined (ARRAY_VARS) +static SHELL_VAR *do_compound_assignment __P((char *, char *, int)); +#endif +static int do_assignment_internal __P((const WORD_DESC *, int)); + +static char *string_extract_verbatim __P((char *, size_t, int *, char *, int)); +static char *string_extract __P((char *, int *, char *, int)); +static char *string_extract_double_quoted __P((char *, int *, int)); +static inline char *string_extract_single_quoted __P((char *, int *)); +static inline int skip_single_quoted __P((const char *, size_t, int)); +static int skip_double_quoted __P((char *, size_t, int)); +static char *extract_delimited_string __P((char *, int *, char *, char *, char *, int)); +static char *extract_dollar_brace_string __P((char *, int *, int, int)); +static int skip_matched_pair __P((const char *, int, int, int, int)); + +static char *pos_params __P((char *, int, int, int)); + +static unsigned char *mb_getcharlens __P((char *, int)); + +static char *remove_upattern __P((char *, char *, int)); +#if defined (HANDLE_MULTIBYTE) +static wchar_t *remove_wpattern __P((wchar_t *, size_t, wchar_t *, int)); +#endif +static char *remove_pattern __P((char *, char *, int)); + +static int match_pattern_char __P((char *, char *)); +static int match_upattern __P((char *, char *, int, char **, char **)); +#if defined (HANDLE_MULTIBYTE) +static int match_pattern_wchar __P((wchar_t *, wchar_t *)); +static int match_wpattern __P((wchar_t *, char **, size_t, wchar_t *, int, char **, char **)); +#endif +static int match_pattern __P((char *, char *, int, char **, char **)); +static int getpatspec __P((int, char *)); +static char *getpattern __P((char *, int, int)); +static char *variable_remove_pattern __P((char *, char *, int, int)); +static char *list_remove_pattern __P((WORD_LIST *, char *, int, int, int)); +static char *parameter_list_remove_pattern __P((int, char *, int, int)); +#ifdef ARRAY_VARS +static char *array_remove_pattern __P((SHELL_VAR *, char *, int, char *, int)); +#endif +static char *parameter_brace_remove_pattern __P((char *, char *, int, char *, int, int, int)); + +static char *process_substitute __P((char *, int)); + +static char *read_comsub __P((int, int, int *)); + +#ifdef ARRAY_VARS +static arrayind_t array_length_reference __P((char *)); +#endif + +static int valid_brace_expansion_word __P((char *, int)); +static int chk_atstar __P((char *, int, int *, int *)); +static int chk_arithsub __P((const char *, int)); + +static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int, arrayind_t *)); +static WORD_DESC *parameter_brace_expand_indir __P((char *, int, int, int *, int *)); +static WORD_DESC *parameter_brace_expand_rhs __P((char *, char *, int, int, int *, int *)); +static void parameter_brace_expand_error __P((char *, char *)); + +static int valid_length_expression __P((char *)); +static intmax_t parameter_brace_expand_length __P((char *)); + +static char *skiparith __P((char *, int)); +static int verify_substring_values __P((SHELL_VAR *, char *, char *, int, intmax_t *, intmax_t *)); +static int get_var_and_type __P((char *, char *, arrayind_t, int, int, SHELL_VAR **, char **)); +static char *mb_substring __P((char *, int, int)); +static char *parameter_brace_substring __P((char *, char *, int, char *, int, int)); + +static int shouldexp_replacement __P((char *)); + +static char *pos_params_pat_subst __P((char *, char *, char *, int)); + +static char *parameter_brace_patsub __P((char *, char *, int, char *, int, int)); + +static char *pos_params_casemod __P((char *, char *, int, int)); +static char *parameter_brace_casemod __P((char *, char *, int, int, char *, int, int)); + +static WORD_DESC *parameter_brace_expand __P((char *, int *, int, int, int *, int *)); +static WORD_DESC *param_expand __P((char *, int *, int, int *, int *, int *, int *, int)); + +static WORD_LIST *expand_word_internal __P((WORD_DESC *, int, int, int *, int *)); + +static WORD_LIST *word_list_split __P((WORD_LIST *)); + +static void exp_jump_to_top_level __P((int)); + +static WORD_LIST *separate_out_assignments __P((WORD_LIST *)); +static WORD_LIST *glob_expand_word_list __P((WORD_LIST *, int)); +#ifdef BRACE_EXPANSION +static WORD_LIST *brace_expand_word_list __P((WORD_LIST *, int)); +#endif +#if defined (ARRAY_VARS) +static int make_internal_declare __P((char *, char *)); +#endif +static WORD_LIST *shell_expand_word_list __P((WORD_LIST *, int)); +static WORD_LIST *expand_word_list_internal __P((WORD_LIST *, int)); + +/* **************************************************************** */ +/* */ +/* Utility Functions */ +/* */ +/* **************************************************************** */ + +#if defined (DEBUG) +void +dump_word_flags (flags) + int flags; +{ + int f; + + f = flags; + fprintf (stderr, "%d -> ", f); + if (f & W_ASSIGNASSOC) + { + f &= ~W_ASSIGNASSOC; + fprintf (stderr, "W_ASSIGNASSOC%s", f ? "|" : ""); + } + if (f & W_HASCTLESC) + { + f &= ~W_HASCTLESC; + fprintf (stderr, "W_HASCTLESC%s", f ? "|" : ""); + } + if (f & W_NOPROCSUB) + { + f &= ~W_NOPROCSUB; + fprintf (stderr, "W_NOPROCSUB%s", f ? "|" : ""); + } + if (f & W_DQUOTE) + { + f &= ~W_DQUOTE; + fprintf (stderr, "W_DQUOTE%s", f ? "|" : ""); + } + if (f & W_HASQUOTEDNULL) + { + f &= ~W_HASQUOTEDNULL; + fprintf (stderr, "W_HASQUOTEDNULL%s", f ? "|" : ""); + } + if (f & W_ASSIGNARG) + { + f &= ~W_ASSIGNARG; + fprintf (stderr, "W_ASSIGNARG%s", f ? "|" : ""); + } + if (f & W_ASSNBLTIN) + { + f &= ~W_ASSNBLTIN; + fprintf (stderr, "W_ASSNBLTIN%s", f ? "|" : ""); + } + if (f & W_COMPASSIGN) + { + f &= ~W_COMPASSIGN; + fprintf (stderr, "W_COMPASSIGN%s", f ? "|" : ""); + } + if (f & W_NOEXPAND) + { + f &= ~W_NOEXPAND; + fprintf (stderr, "W_NOEXPAND%s", f ? "|" : ""); + } + if (f & W_ITILDE) + { + f &= ~W_ITILDE; + fprintf (stderr, "W_ITILDE%s", f ? "|" : ""); + } + if (f & W_NOTILDE) + { + f &= ~W_NOTILDE; + fprintf (stderr, "W_NOTILDE%s", f ? "|" : ""); + } + if (f & W_ASSIGNRHS) + { + f &= ~W_ASSIGNRHS; + fprintf (stderr, "W_ASSIGNRHS%s", f ? "|" : ""); + } + if (f & W_NOCOMSUB) + { + f &= ~W_NOCOMSUB; + fprintf (stderr, "W_NOCOMSUB%s", f ? "|" : ""); + } + if (f & W_DOLLARSTAR) + { + f &= ~W_DOLLARSTAR; + fprintf (stderr, "W_DOLLARSTAR%s", f ? "|" : ""); + } + if (f & W_DOLLARAT) + { + f &= ~W_DOLLARAT; + fprintf (stderr, "W_DOLLARAT%s", f ? "|" : ""); + } + if (f & W_TILDEEXP) + { + f &= ~W_TILDEEXP; + fprintf (stderr, "W_TILDEEXP%s", f ? "|" : ""); + } + if (f & W_NOSPLIT2) + { + f &= ~W_NOSPLIT2; + fprintf (stderr, "W_NOSPLIT2%s", f ? "|" : ""); + } + if (f & W_NOGLOB) + { + f &= ~W_NOGLOB; + fprintf (stderr, "W_NOGLOB%s", f ? "|" : ""); + } + if (f & W_NOSPLIT) + { + f &= ~W_NOSPLIT; + fprintf (stderr, "W_NOSPLIT%s", f ? "|" : ""); + } + if (f & W_GLOBEXP) + { + f &= ~W_GLOBEXP; + fprintf (stderr, "W_GLOBEXP%s", f ? "|" : ""); + } + if (f & W_ASSIGNMENT) + { + f &= ~W_ASSIGNMENT; + fprintf (stderr, "W_ASSIGNMENT%s", f ? "|" : ""); + } + if (f & W_QUOTED) + { + f &= ~W_QUOTED; + fprintf (stderr, "W_QUOTED%s", f ? "|" : ""); + } + if (f & W_HASDOLLAR) + { + f &= ~W_HASDOLLAR; + fprintf (stderr, "W_HASDOLLAR%s", f ? "|" : ""); + } + fprintf (stderr, "\n"); + fflush (stderr); +} +#endif + +#ifdef INCLUDE_UNUSED +static char * +quoted_substring (string, start, end) + char *string; + int start, end; +{ + register int len, l; + register char *result, *s, *r; + + len = end - start; + + /* Move to string[start], skipping quoted characters. */ + for (s = string, l = 0; *s && l < start; ) + { + if (*s == CTLESC) + { + s++; + continue; + } + l++; + if (*s == 0) + break; + } + + r = result = (char *)xmalloc (2*len + 1); /* save room for quotes */ + + /* Copy LEN characters, including quote characters. */ + s = string + l; + for (l = 0; l < len; s++) + { + if (*s == CTLESC) + *r++ = *s++; + *r++ = *s; + l++; + if (*s == 0) + break; + } + *r = '\0'; + return result; +} +#endif + +#ifdef INCLUDE_UNUSED +/* Return the length of S, skipping over quoted characters */ +static int +quoted_strlen (s) + char *s; +{ + register char *p; + int i; + + i = 0; + for (p = s; *p; p++) + { + if (*p == CTLESC) + { + p++; + if (*p == 0) + return (i + 1); + } + i++; + } + + return i; +} +#endif + +/* Find the first occurrence of character C in string S, obeying shell + quoting rules. If (FLAGS & ST_BACKSL) is non-zero, backslash-escaped + characters are skipped. If (FLAGS & ST_CTLESC) is non-zero, characters + escaped with CTLESC are skipped. */ +static char * +quoted_strchr (s, c, flags) + char *s; + int c, flags; +{ + register char *p; + + for (p = s; *p; p++) + { + if (((flags & ST_BACKSL) && *p == '\\') + || ((flags & ST_CTLESC) && *p == CTLESC)) + { + p++; + if (*p == '\0') + return ((char *)NULL); + continue; + } + else if (*p == c) + return p; + } + return ((char *)NULL); +} + +/* Return 1 if CHARACTER appears in an unquoted portion of + STRING. Return 0 otherwise. CHARACTER must be a single-byte character. */ +static int +unquoted_member (character, string) + int character; + char *string; +{ + size_t slen; + int sindex, c; + DECLARE_MBSTATE; + + slen = strlen (string); + sindex = 0; + while (c = string[sindex]) + { + if (c == character) + return (1); + + switch (c) + { + default: + ADVANCE_CHAR (string, slen, sindex); + break; + + case '\\': + sindex++; + if (string[sindex]) + ADVANCE_CHAR (string, slen, sindex); + break; + + case '\'': + sindex = skip_single_quoted (string, slen, ++sindex); + break; + + case '"': + sindex = skip_double_quoted (string, slen, ++sindex); + break; + } + } + return (0); +} + +/* Return 1 if SUBSTR appears in an unquoted portion of STRING. */ +static int +unquoted_substring (substr, string) + char *substr, *string; +{ + size_t slen; + int sindex, c, sublen; + DECLARE_MBSTATE; + + if (substr == 0 || *substr == '\0') + return (0); + + slen = strlen (string); + sublen = strlen (substr); + for (sindex = 0; c = string[sindex]; ) + { + if (STREQN (string + sindex, substr, sublen)) + return (1); + + switch (c) + { + case '\\': + sindex++; + + if (string[sindex]) + ADVANCE_CHAR (string, slen, sindex); + break; + + case '\'': + sindex = skip_single_quoted (string, slen, ++sindex); + break; + + case '"': + sindex = skip_double_quoted (string, slen, ++sindex); + break; + + default: + ADVANCE_CHAR (string, slen, sindex); + break; + } + } + return (0); +} + +/* Most of the substitutions must be done in parallel. In order + to avoid using tons of unclear goto's, I have some functions + for manipulating malloc'ed strings. They all take INDX, a + pointer to an integer which is the offset into the string + where manipulation is taking place. They also take SIZE, a + pointer to an integer which is the current length of the + character array for this string. */ + +/* Append SOURCE to TARGET at INDEX. SIZE is the current amount + of space allocated to TARGET. SOURCE can be NULL, in which + case nothing happens. Gets rid of SOURCE by freeing it. + Returns TARGET in case the location has changed. */ +INLINE char * +sub_append_string (source, target, indx, size) + char *source, *target; + int *indx, *size; +{ + if (source) + { + int srclen, n; + + srclen = STRLEN (source); + if (srclen >= (int)(*size - *indx)) + { + n = srclen + *indx; + n = (n + DEFAULT_ARRAY_SIZE) - (n % DEFAULT_ARRAY_SIZE); + target = (char *)xrealloc (target, (*size = n)); + } + + FASTCOPY (source, target + *indx, srclen); + *indx += srclen; + target[*indx] = '\0'; + + free (source); + } + return (target); +} + +#if 0 +/* UNUSED */ +/* Append the textual representation of NUMBER to TARGET. + INDX and SIZE are as in SUB_APPEND_STRING. */ +char * +sub_append_number (number, target, indx, size) + intmax_t number; + int *indx, *size; + char *target; +{ + char *temp; + + temp = itos (number); + return (sub_append_string (temp, target, indx, size)); +} +#endif + +/* Extract a substring from STRING, starting at SINDEX and ending with + one of the characters in CHARLIST. Don't make the ending character + part of the string. Leave SINDEX pointing at the ending character. + Understand about backslashes in the string. If (flags & SX_VARNAME) + is non-zero, and array variables have been compiled into the shell, + everything between a `[' and a corresponding `]' is skipped over. + If (flags & SX_NOALLOC) is non-zero, don't return the substring, just + update SINDEX. If (flags & SX_REQMATCH) is non-zero, the string must + contain a closing character from CHARLIST. */ +static char * +string_extract (string, sindex, charlist, flags) + char *string; + int *sindex; + char *charlist; + int flags; +{ + register int c, i; + int found; + size_t slen; + char *temp; + DECLARE_MBSTATE; + + slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 0; + i = *sindex; + found = 0; + while (c = string[i]) + { + if (c == '\\') + { + if (string[i + 1]) + i++; + else + break; + } +#if defined (ARRAY_VARS) + else if ((flags & SX_VARNAME) && c == '[') + { + int ni; + /* If this is an array subscript, skip over it and continue. */ + ni = skipsubscript (string, i, 0); + if (string[ni] == ']') + i = ni; + } +#endif + else if (MEMBER (c, charlist)) + { + found = 1; + break; + } + + ADVANCE_CHAR (string, slen, i); + } + + /* If we had to have a matching delimiter and didn't find one, return an + error and let the caller deal with it. */ + if ((flags & SX_REQMATCH) && found == 0) + { + *sindex = i; + return (&extract_string_error); + } + + temp = (flags & SX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i); + *sindex = i; + + return (temp); +} + +/* Extract the contents of STRING as if it is enclosed in double quotes. + SINDEX, when passed in, is the offset of the character immediately + following the opening double quote; on exit, SINDEX is left pointing after + the closing double quote. If STRIPDQ is non-zero, unquoted double + quotes are stripped and the string is terminated by a null byte. + Backslashes between the embedded double quotes are processed. If STRIPDQ + is zero, an unquoted `"' terminates the string. */ +static char * +string_extract_double_quoted (string, sindex, stripdq) + char *string; + int *sindex, stripdq; +{ + size_t slen; + char *send; + int j, i, t; + unsigned char c; + char *temp, *ret; /* The new string we return. */ + int pass_next, backquote, si; /* State variables for the machine. */ + int dquote; + DECLARE_MBSTATE; + + slen = strlen (string + *sindex) + *sindex; + send = string + slen; + + pass_next = backquote = dquote = 0; + temp = (char *)xmalloc (1 + slen - *sindex); + + j = 0; + i = *sindex; + while (c = string[i]) + { + /* Process a character that was quoted by a backslash. */ + if (pass_next) + { + /* XXX - take another look at this in light of Interp 221 */ + /* Posix.2 sez: + + ``The backslash shall retain its special meaning as an escape + character only when followed by one of the characters: + $ ` " \ <newline>''. + + If STRIPDQ is zero, we handle the double quotes here and let + expand_word_internal handle the rest. If STRIPDQ is non-zero, + we have already been through one round of backslash stripping, + and want to strip these backslashes only if DQUOTE is non-zero, + indicating that we are inside an embedded double-quoted string. */ + + /* If we are in an embedded quoted string, then don't strip + backslashes before characters for which the backslash + retains its special meaning, but remove backslashes in + front of other characters. If we are not in an + embedded quoted string, don't strip backslashes at all. + This mess is necessary because the string was already + surrounded by double quotes (and sh has some really weird + quoting rules). + The returned string will be run through expansion as if + it were double-quoted. */ + if ((stripdq == 0 && c != '"') || + (stripdq && ((dquote && (sh_syntaxtab[c] & CBSDQUOTE)) || dquote == 0))) + temp[j++] = '\\'; + pass_next = 0; + +add_one_character: + COPY_CHAR_I (temp, j, string, send, i); + continue; + } + + /* A backslash protects the next character. The code just above + handles preserving the backslash in front of any character but + a double quote. */ + if (c == '\\') + { + pass_next++; + i++; + continue; + } + + /* Inside backquotes, ``the portion of the quoted string from the + initial backquote and the characters up to the next backquote + that is not preceded by a backslash, having escape characters + removed, defines that command''. */ + if (backquote) + { + if (c == '`') + backquote = 0; + temp[j++] = c; + i++; + continue; + } + + if (c == '`') + { + temp[j++] = c; + backquote++; + i++; + continue; + } + + /* Pass everything between `$(' and the matching `)' or a quoted + ${ ... } pair through according to the Posix.2 specification. */ + if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE))) + { + int free_ret = 1; + + si = i + 2; + if (string[i + 1] == LPAREN) + ret = extract_command_subst (string, &si, 0); + else + ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, 0); + + temp[j++] = '$'; + temp[j++] = string[i + 1]; + + /* Just paranoia; ret will not be 0 unless no_longjmp_on_fatal_error + is set. */ + if (ret == 0 && no_longjmp_on_fatal_error) + { + free_ret = 0; + ret = string + i + 2; + } + + for (t = 0; ret[t]; t++, j++) + temp[j] = ret[t]; + temp[j] = string[si]; + + if (string[si]) + { + j++; + i = si + 1; + } + else + i = si; + + if (free_ret) + free (ret); + continue; + } + + /* Add any character but a double quote to the quoted string we're + accumulating. */ + if (c != '"') + goto add_one_character; + + /* c == '"' */ + if (stripdq) + { + dquote ^= 1; + i++; + continue; + } + + break; + } + temp[j] = '\0'; + + /* Point to after the closing quote. */ + if (c) + i++; + *sindex = i; + + return (temp); +} + +/* This should really be another option to string_extract_double_quoted. */ +static int +skip_double_quoted (string, slen, sind) + char *string; + size_t slen; + int sind; +{ + int c, i; + char *ret; + int pass_next, backquote, si; + DECLARE_MBSTATE; + + pass_next = backquote = 0; + i = sind; + while (c = string[i]) + { + if (pass_next) + { + pass_next = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next++; + i++; + continue; + } + else if (backquote) + { + if (c == '`') + backquote = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '`') + { + backquote++; + i++; + continue; + } + else if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE))) + { + si = i + 2; + if (string[i + 1] == LPAREN) + ret = extract_command_subst (string, &si, SX_NOALLOC); + else + ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC); + + i = si + 1; + continue; + } + else if (c != '"') + { + ADVANCE_CHAR (string, slen, i); + continue; + } + else + break; + } + + if (c) + i++; + + return (i); +} + +/* Extract the contents of STRING as if it is enclosed in single quotes. + SINDEX, when passed in, is the offset of the character immediately + following the opening single quote; on exit, SINDEX is left pointing after + the closing single quote. */ +static inline char * +string_extract_single_quoted (string, sindex) + char *string; + int *sindex; +{ + register int i; + size_t slen; + char *t; + DECLARE_MBSTATE; + + /* Don't need slen for ADVANCE_CHAR unless multibyte chars possible. */ + slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 0; + i = *sindex; + while (string[i] && string[i] != '\'') + ADVANCE_CHAR (string, slen, i); + + t = substring (string, *sindex, i); + + if (string[i]) + i++; + *sindex = i; + + return (t); +} + +static inline int +skip_single_quoted (string, slen, sind) + const char *string; + size_t slen; + int sind; +{ + register int c; + DECLARE_MBSTATE; + + c = sind; + while (string[c] && string[c] != '\'') + ADVANCE_CHAR (string, slen, c); + + if (string[c]) + c++; + return c; +} + +/* Just like string_extract, but doesn't hack backslashes or any of + that other stuff. Obeys CTLESC quoting. Used to do splitting on $IFS. */ +static char * +string_extract_verbatim (string, slen, sindex, charlist, flags) + char *string; + size_t slen; + int *sindex; + char *charlist; + int flags; +{ + register int i; +#if defined (HANDLE_MULTIBYTE) + size_t clen; + wchar_t *wcharlist; +#endif + int c; + char *temp; + DECLARE_MBSTATE; + + if (charlist[0] == '\'' && charlist[1] == '\0') + { + temp = string_extract_single_quoted (string, sindex); + --*sindex; /* leave *sindex at separator character */ + return temp; + } + + i = *sindex; +#if 0 + /* See how the MBLEN and ADVANCE_CHAR macros work to understand why we need + this only if MB_CUR_MAX > 1. */ + slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 1; +#endif +#if defined (HANDLE_MULTIBYTE) + clen = strlen (charlist); + wcharlist = 0; +#endif + while (c = string[i]) + { +#if defined (HANDLE_MULTIBYTE) + size_t mblength; +#endif + if ((flags & SX_NOCTLESC) == 0 && c == CTLESC) + { + i += 2; + continue; + } + /* Even if flags contains SX_NOCTLESC, we let CTLESC quoting CTLNUL + through, to protect the CTLNULs from later calls to + remove_quoted_nulls. */ + else if ((flags & SX_NOESCCTLNUL) == 0 && c == CTLESC && string[i+1] == CTLNUL) + { + i += 2; + continue; + } + +#if defined (HANDLE_MULTIBYTE) + mblength = MBLEN (string + i, slen - i); + if (mblength > 1) + { + wchar_t wc; + mblength = mbtowc (&wc, string + i, slen - i); + if (MB_INVALIDCH (mblength)) + { + if (MEMBER (c, charlist)) + break; + } + else + { + if (wcharlist == 0) + { + size_t len; + len = mbstowcs (wcharlist, charlist, 0); + if (len == -1) + len = 0; + wcharlist = (wchar_t *)xmalloc (sizeof (wchar_t) * (len + 1)); + mbstowcs (wcharlist, charlist, len + 1); + } + + if (wcschr (wcharlist, wc)) + break; + } + } + else +#endif + if (MEMBER (c, charlist)) + break; + + ADVANCE_CHAR (string, slen, i); + } + +#if defined (HANDLE_MULTIBYTE) + FREE (wcharlist); +#endif + + temp = substring (string, *sindex, i); + *sindex = i; + + return (temp); +} + +/* Extract the $( construct in STRING, and return a new string. + Start extracting at (SINDEX) as if we had just seen "$(". + Make (SINDEX) get the position of the matching ")". ) + XFLAGS is additional flags to pass to other extraction functions. */ +char * +extract_command_subst (string, sindex, xflags) + char *string; + int *sindex; + int xflags; +{ + if (string[*sindex] == LPAREN) + return (extract_delimited_string (string, sindex, "$(", "(", ")", xflags|SX_COMMAND)); /*)*/ + else + { + xflags |= (no_longjmp_on_fatal_error ? SX_NOLONGJMP : 0); + return (xparse_dolparen (string, string+*sindex, sindex, xflags)); + } +} + +/* Extract the $[ construct in STRING, and return a new string. (]) + Start extracting at (SINDEX) as if we had just seen "$[". + Make (SINDEX) get the position of the matching "]". */ +char * +extract_arithmetic_subst (string, sindex) + char *string; + int *sindex; +{ + return (extract_delimited_string (string, sindex, "$[", "[", "]", 0)); /*]*/ +} + +#if defined (PROCESS_SUBSTITUTION) +/* Extract the <( or >( construct in STRING, and return a new string. + Start extracting at (SINDEX) as if we had just seen "<(". + Make (SINDEX) get the position of the matching ")". */ /*))*/ +char * +extract_process_subst (string, starter, sindex) + char *string; + char *starter; + int *sindex; +{ + return (extract_delimited_string (string, sindex, starter, "(", ")", 0)); +} +#endif /* PROCESS_SUBSTITUTION */ + +#if defined (ARRAY_VARS) +/* This can be fooled by unquoted right parens in the passed string. If + each caller verifies that the last character in STRING is a right paren, + we don't even need to call extract_delimited_string. */ +char * +extract_array_assignment_list (string, sindex) + char *string; + int *sindex; +{ + int slen; + char *ret; + + slen = strlen (string); /* ( */ + if (string[slen - 1] == ')') + { + ret = substring (string, *sindex, slen - 1); + *sindex = slen - 1; + return ret; + } + return 0; +} +#endif + +/* Extract and create a new string from the contents of STRING, a + character string delimited with OPENER and CLOSER. SINDEX is + the address of an int describing the current offset in STRING; + it should point to just after the first OPENER found. On exit, + SINDEX gets the position of the last character of the matching CLOSER. + If OPENER is more than a single character, ALT_OPENER, if non-null, + contains a character string that can also match CLOSER and thus + needs to be skipped. */ +static char * +extract_delimited_string (string, sindex, opener, alt_opener, closer, flags) + char *string; + int *sindex; + char *opener, *alt_opener, *closer; + int flags; +{ + int i, c, si; + size_t slen; + char *t, *result; + int pass_character, nesting_level, in_comment; + int len_closer, len_opener, len_alt_opener; + DECLARE_MBSTATE; + + slen = strlen (string + *sindex) + *sindex; + len_opener = STRLEN (opener); + len_alt_opener = STRLEN (alt_opener); + len_closer = STRLEN (closer); + + pass_character = in_comment = 0; + + nesting_level = 1; + i = *sindex; + + while (nesting_level) + { + c = string[i]; + + if (c == 0) + break; + + if (in_comment) + { + if (c == '\n') + in_comment = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + + if (pass_character) /* previous char was backslash */ + { + pass_character = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + + /* Not exactly right yet; should handle shell metacharacters and + multibyte characters, too. See COMMENT_BEGIN define in parse.y */ + if ((flags & SX_COMMAND) && c == '#' && (i == 0 || string[i - 1] == '\n' || shellblank (string[i - 1]))) + { + in_comment = 1; + ADVANCE_CHAR (string, slen, i); + continue; + } + + if (c == CTLESC || c == '\\') + { + pass_character++; + i++; + continue; + } + + /* Process a nested command substitution, but only if we're parsing an + arithmetic substitution. */ + if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LPAREN) + { + si = i + 2; + t = extract_command_subst (string, &si, flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Process a nested OPENER. */ + if (STREQN (string + i, opener, len_opener)) + { + si = i + len_opener; + t = extract_delimited_string (string, &si, opener, alt_opener, closer, flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Process a nested ALT_OPENER */ + if (len_alt_opener && STREQN (string + i, alt_opener, len_alt_opener)) + { + si = i + len_alt_opener; + t = extract_delimited_string (string, &si, alt_opener, alt_opener, closer, flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* If the current substring terminates the delimited string, decrement + the nesting level. */ + if (STREQN (string + i, closer, len_closer)) + { + i += len_closer - 1; /* move to last byte of the closer */ + nesting_level--; + if (nesting_level == 0) + break; + } + + /* Pass old-style command substitution through verbatim. */ + if (c == '`') + { + si = i + 1; + t = string_extract (string, &si, "`", flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Pass single-quoted and double-quoted strings through verbatim. */ + if (c == '\'' || c == '"') + { + si = i + 1; + i = (c == '\'') ? skip_single_quoted (string, slen, si) + : skip_double_quoted (string, slen, si); + continue; + } + + /* move past this character, which was not special. */ + ADVANCE_CHAR (string, slen, i); + } + + if (c == 0 && nesting_level) + { + if (no_longjmp_on_fatal_error == 0) + { + report_error (_("bad substitution: no closing `%s' in %s"), closer, string); + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level (DISCARD); + } + else + { + *sindex = i; + return (char *)NULL; + } + } + + si = i - *sindex - len_closer + 1; + if (flags & SX_NOALLOC) + result = (char *)NULL; + else + { + result = (char *)xmalloc (1 + si); + strncpy (result, string + *sindex, si); + result[si] = '\0'; + } + *sindex = i; + + return (result); +} + +/* Extract a parameter expansion expression within ${ and } from STRING. + Obey the Posix.2 rules for finding the ending `}': count braces while + skipping over enclosed quoted strings and command substitutions. + SINDEX is the address of an int describing the current offset in STRING; + it should point to just after the first `{' found. On exit, SINDEX + gets the position of the matching `}'. QUOTED is non-zero if this + occurs inside double quotes. */ +/* XXX -- this is very similar to extract_delimited_string -- XXX */ +static char * +extract_dollar_brace_string (string, sindex, quoted, flags) + char *string; + int *sindex, quoted, flags; +{ + register int i, c; + size_t slen; + int pass_character, nesting_level, si, dolbrace_state; + char *result, *t; + DECLARE_MBSTATE; + + pass_character = 0; + nesting_level = 1; + slen = strlen (string + *sindex) + *sindex; + + /* The handling of dolbrace_state needs to agree with the code in parse.y: + parse_matched_pair() */ + dolbrace_state = 0; + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + dolbrace_state = (flags & SX_POSIXEXP) ? DOLBRACE_QUOTE : DOLBRACE_PARAM; + + i = *sindex; + while (c = string[i]) + { + if (pass_character) + { + pass_character = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + + /* CTLESCs and backslashes quote the next character. */ + if (c == CTLESC || c == '\\') + { + pass_character++; + i++; + continue; + } + + if (string[i] == '$' && string[i+1] == LBRACE) + { + nesting_level++; + i += 2; + continue; + } + + if (c == RBRACE) + { + nesting_level--; + if (nesting_level == 0) + break; + i++; + continue; + } + + /* Pass the contents of old-style command substitutions through + verbatim. */ + if (c == '`') + { + si = i + 1; + t = string_extract (string, &si, "`", flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Pass the contents of new-style command substitutions and + arithmetic substitutions through verbatim. */ + if (string[i] == '$' && string[i+1] == LPAREN) + { + si = i + 2; + t = extract_command_subst (string, &si, flags|SX_NOALLOC); + i = si + 1; + continue; + } + +#if 0 + /* Pass the contents of single-quoted and double-quoted strings + through verbatim. */ + if (c == '\'' || c == '"') + { + si = i + 1; + i = (c == '\'') ? skip_single_quoted (string, slen, si) + : skip_double_quoted (string, slen, si); + /* skip_XXX_quoted leaves index one past close quote */ + continue; + } +#else /* XXX - bash-4.2 */ + /* Pass the contents of double-quoted strings through verbatim. */ + if (c == '"') + { + si = i + 1; + i = skip_double_quoted (string, slen, si); + /* skip_XXX_quoted leaves index one past close quote */ + continue; + } + + if (c == '\'') + { +/*itrace("extract_dollar_brace_string: c == single quote flags = %d quoted = %d dolbrace_state = %d", flags, quoted, dolbrace_state);*/ + if (posixly_correct && shell_compatibility_level > 41 && dolbrace_state != DOLBRACE_QUOTE) + ADVANCE_CHAR (string, slen, i); + else + { + si = i + 1; + i = skip_single_quoted (string, slen, si); + } + + continue; + } +#endif + + /* move past this character, which was not special. */ + ADVANCE_CHAR (string, slen, i); + + /* This logic must agree with parse.y:parse_matched_pair, since they + share the same defines. */ + if (dolbrace_state == DOLBRACE_PARAM && c == '%' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == '#' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == '/' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == '^' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == ',' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", c) != 0) + dolbrace_state = DOLBRACE_OP; + else if (dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", c) == 0) + dolbrace_state = DOLBRACE_WORD; + } + + if (c == 0 && nesting_level) + { + if (no_longjmp_on_fatal_error == 0) + { /* { */ + report_error (_("bad substitution: no closing `%s' in %s"), "}", string); + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level (DISCARD); + } + else + { + *sindex = i; + return ((char *)NULL); + } + } + + result = (flags & SX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i); + *sindex = i; + + return (result); +} + +/* Remove backslashes which are quoting backquotes from STRING. Modifies + STRING, and returns a pointer to it. */ +char * +de_backslash (string) + char *string; +{ + register size_t slen; + register int i, j, prev_i; + DECLARE_MBSTATE; + + slen = strlen (string); + i = j = 0; + + /* Loop copying string[i] to string[j], i >= j. */ + while (i < slen) + { + if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' || + string[i + 1] == '$')) + i++; + prev_i = i; + ADVANCE_CHAR (string, slen, i); + if (j < prev_i) + do string[j++] = string[prev_i++]; while (prev_i < i); + else + j = i; + } + string[j] = '\0'; + + return (string); +} + +#if 0 +/*UNUSED*/ +/* Replace instances of \! in a string with !. */ +void +unquote_bang (string) + char *string; +{ + register int i, j; + register char *temp; + + temp = (char *)xmalloc (1 + strlen (string)); + + for (i = 0, j = 0; (temp[j] = string[i]); i++, j++) + { + if (string[i] == '\\' && string[i + 1] == '!') + { + temp[j] = '!'; + i++; + } + } + strcpy (string, temp); + free (temp); +} +#endif + +#define CQ_RETURN(x) do { no_longjmp_on_fatal_error = 0; return (x); } while (0) + +/* This function assumes s[i] == open; returns with s[ret] == close; used to + parse array subscripts. FLAGS & 1 means to not attempt to skip over + matched pairs of quotes or backquotes, or skip word expansions; it is + intended to be used after expansion has been performed and during final + assignment parsing (see arrayfunc.c:assign_compound_array_list()). */ +static int +skip_matched_pair (string, start, open, close, flags) + const char *string; + int start, open, close, flags; +{ + int i, pass_next, backq, si, c, count; + size_t slen; + char *temp, *ss; + DECLARE_MBSTATE; + + slen = strlen (string + start) + start; + no_longjmp_on_fatal_error = 1; + + i = start + 1; /* skip over leading bracket */ + count = 1; + pass_next = backq = 0; + ss = (char *)string; + while (c = string[i]) + { + if (pass_next) + { + pass_next = 0; + if (c == 0) + CQ_RETURN(i); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (backq) + { + if (c == '`') + backq = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if ((flags & 1) == 0 && c == '`') + { + backq = 1; + i++; + continue; + } + else if ((flags & 1) == 0 && c == open) + { + count++; + i++; + continue; + } + else if (c == close) + { + count--; + if (count == 0) + break; + i++; + continue; + } + else if ((flags & 1) == 0 && (c == '\'' || c == '"')) + { + i = (c == '\'') ? skip_single_quoted (ss, slen, ++i) + : skip_double_quoted (ss, slen, ++i); + /* no increment, the skip functions increment past the closing quote. */ + } + else if ((flags&1) == 0 && c == '$' && (string[i+1] == LPAREN || string[i+1] == LBRACE)) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + + if (string[i+1] == LPAREN) + temp = extract_delimited_string (ss, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */ + else + temp = extract_dollar_brace_string (ss, &si, 0, SX_NOALLOC); + i = si; + if (string[i] == '\0') /* don't increment i past EOS in loop */ + break; + i++; + continue; + } + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(i); +} + +#if defined (ARRAY_VARS) +int +skipsubscript (string, start, flags) + const char *string; + int start, flags; +{ + return (skip_matched_pair (string, start, '[', ']', flags)); +} +#endif + +/* Skip characters in STRING until we find a character in DELIMS, and return + the index of that character. START is the index into string at which we + begin. This is similar in spirit to strpbrk, but it returns an index into + STRING and takes a starting index. This little piece of code knows quite + a lot of shell syntax. It's very similar to skip_double_quoted and other + functions of that ilk. */ +int +skip_to_delim (string, start, delims, flags) + char *string; + int start; + char *delims; + int flags; +{ + int i, pass_next, backq, si, c, invert, skipquote, skipcmd; + size_t slen; + char *temp; + DECLARE_MBSTATE; + + slen = strlen (string + start) + start; + if (flags & SD_NOJMP) + no_longjmp_on_fatal_error = 1; + invert = (flags & SD_INVERT); + skipcmd = (flags & SD_NOSKIPCMD) == 0; + + i = start; + pass_next = backq = 0; + while (c = string[i]) + { + /* If this is non-zero, we should not let quote characters be delimiters + and the current character is a single or double quote. We should not + test whether or not it's a delimiter until after we skip single- or + double-quoted strings. */ + skipquote = ((flags & SD_NOQUOTEDELIM) && (c == '\'' || c =='"')); + if (pass_next) + { + pass_next = 0; + if (c == 0) + CQ_RETURN(i); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (backq) + { + if (c == '`') + backq = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '`') + { + backq = 1; + i++; + continue; + } + else if (skipquote == 0 && invert == 0 && member (c, delims)) + break; + else if (c == '\'' || c == '"') + { + i = (c == '\'') ? skip_single_quoted (string, slen, ++i) + : skip_double_quoted (string, slen, ++i); + /* no increment, the skip functions increment past the closing quote. */ + } + else if (c == '$' && ((skipcmd && string[i+1] == LPAREN) || string[i+1] == LBRACE)) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + + if (string[i+1] == LPAREN) + temp = extract_delimited_string (string, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */ + else + temp = extract_dollar_brace_string (string, &si, 0, SX_NOALLOC); + i = si; + if (string[i] == '\0') /* don't increment i past EOS in loop */ + break; + i++; + continue; + } +#if defined (PROCESS_SUBSTITUTION) + else if (skipcmd && (c == '<' || c == '>') && string[i+1] == LPAREN) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + temp = extract_process_subst (string, (c == '<') ? "<(" : ">(", &si); + i = si; + if (string[i] == '\0') + break; + i++; + continue; + } +#endif /* PROCESS_SUBSTITUTION */ + else if ((skipquote || invert) && (member (c, delims) == 0)) + break; + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(i); +} + +#if defined (READLINE) +/* Return 1 if the portion of STRING ending at EINDEX is quoted (there is + an unclosed quoted string), or if the character at EINDEX is quoted + by a backslash. NO_LONGJMP_ON_FATAL_ERROR is used to flag that the various + single and double-quoted string parsing functions should not return an + error if there are unclosed quotes or braces. The characters that this + recognizes need to be the same as the contents of + rl_completer_quote_characters. */ + +int +char_is_quoted (string, eindex) + char *string; + int eindex; +{ + int i, pass_next, c; + size_t slen; + DECLARE_MBSTATE; + + slen = strlen (string); + no_longjmp_on_fatal_error = 1; + i = pass_next = 0; + while (i <= eindex) + { + c = string[i]; + + if (pass_next) + { + pass_next = 0; + if (i >= eindex) /* XXX was if (i >= eindex - 1) */ + CQ_RETURN(1); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (c == '\'' || c == '"') + { + i = (c == '\'') ? skip_single_quoted (string, slen, ++i) + : skip_double_quoted (string, slen, ++i); + if (i > eindex) + CQ_RETURN(1); + /* no increment, the skip_xxx functions go one past end */ + } + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(0); +} + +int +unclosed_pair (string, eindex, openstr) + char *string; + int eindex; + char *openstr; +{ + int i, pass_next, openc, olen; + size_t slen; + DECLARE_MBSTATE; + + slen = strlen (string); + olen = strlen (openstr); + i = pass_next = openc = 0; + while (i <= eindex) + { + if (pass_next) + { + pass_next = 0; + if (i >= eindex) /* XXX was if (i >= eindex - 1) */ + return 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (string[i] == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (STREQN (string + i, openstr, olen)) + { + openc = 1 - openc; + i += olen; + } + else if (string[i] == '\'' || string[i] == '"') + { + i = (string[i] == '\'') ? skip_single_quoted (string, slen, i) + : skip_double_quoted (string, slen, i); + if (i > eindex) + return 0; + } + else + ADVANCE_CHAR (string, slen, i); + } + return (openc); +} + +/* Split STRING (length SLEN) at DELIMS, and return a WORD_LIST with the + individual words. If DELIMS is NULL, the current value of $IFS is used + to split the string, and the function follows the shell field splitting + rules. SENTINEL is an index to look for. NWP, if non-NULL, + gets the number of words in the returned list. CWP, if non-NULL, gets + the index of the word containing SENTINEL. Non-whitespace chars in + DELIMS delimit separate fields. */ +WORD_LIST * +split_at_delims (string, slen, delims, sentinel, flags, nwp, cwp) + char *string; + int slen; + char *delims; + int sentinel, flags; + int *nwp, *cwp; +{ + int ts, te, i, nw, cw, ifs_split, dflags; + char *token, *d, *d2; + WORD_LIST *ret, *tl; + + if (string == 0 || *string == '\0') + { + if (nwp) + *nwp = 0; + if (cwp) + *cwp = 0; + return ((WORD_LIST *)NULL); + } + + d = (delims == 0) ? ifs_value : delims; + ifs_split = delims == 0; + + /* Make d2 the non-whitespace characters in delims */ + d2 = 0; + if (delims) + { + size_t slength; +#if defined (HANDLE_MULTIBYTE) + size_t mblength = 1; +#endif + DECLARE_MBSTATE; + + slength = strlen (delims); + d2 = (char *)xmalloc (slength + 1); + i = ts = 0; + while (delims[i]) + { +#if defined (HANDLE_MULTIBYTE) + mbstate_t state_bak; + state_bak = state; + mblength = MBRLEN (delims + i, slength, &state); + if (MB_INVALIDCH (mblength)) + state = state_bak; + else if (mblength > 1) + { + memcpy (d2 + ts, delims + i, mblength); + ts += mblength; + i += mblength; + slength -= mblength; + continue; + } +#endif + if (whitespace (delims[i]) == 0) + d2[ts++] = delims[i]; + + i++; + slength--; + } + d2[ts] = '\0'; + } + + ret = (WORD_LIST *)NULL; + + /* Remove sequences of whitespace characters at the start of the string, as + long as those characters are delimiters. */ + for (i = 0; member (string[i], d) && spctabnl (string[i]); i++) + ; + if (string[i] == '\0') + return (ret); + + ts = i; + nw = 0; + cw = -1; + dflags = flags|SD_NOJMP; + while (1) + { + te = skip_to_delim (string, ts, d, dflags); + + /* If we have a non-whitespace delimiter character, use it to make a + separate field. This is just about what $IFS splitting does and + is closer to the behavior of the shell parser. */ + if (ts == te && d2 && member (string[ts], d2)) + { + te = ts + 1; + /* If we're using IFS splitting, the non-whitespace delimiter char + and any additional IFS whitespace delimits a field. */ + if (ifs_split) + while (member (string[te], d) && spctabnl (string[te])) + te++; + else + while (member (string[te], d2)) + te++; + } + + token = substring (string, ts, te); + + ret = add_string_to_list (token, ret); + free (token); + nw++; + + if (sentinel >= ts && sentinel <= te) + cw = nw; + + /* If the cursor is at whitespace just before word start, set the + sentinel word to the current word. */ + if (cwp && cw == -1 && sentinel == ts-1) + cw = nw; + + /* If the cursor is at whitespace between two words, make a new, empty + word, add it before (well, after, since the list is in reverse order) + the word we just added, and set the current word to that one. */ + if (cwp && cw == -1 && sentinel < ts) + { + tl = make_word_list (make_word (""), ret->next); + ret->next = tl; + cw = nw; + nw++; + } + + if (string[te] == 0) + break; + + i = te; + while (member (string[i], d) && (ifs_split || spctabnl(string[i]))) + i++; + + if (string[i]) + ts = i; + else + break; + } + + /* Special case for SENTINEL at the end of STRING. If we haven't found + the word containing SENTINEL yet, and the index we're looking for is at + the end of STRING (or past the end of the previously-found token, + possible if the end of the line is composed solely of IFS whitespace) + add an additional null argument and set the current word pointer to that. */ + if (cwp && cw == -1 && (sentinel >= slen || sentinel >= te)) + { + if (whitespace (string[sentinel - 1])) + { + token = ""; + ret = add_string_to_list (token, ret); + nw++; + } + cw = nw; + } + + if (nwp) + *nwp = nw; + if (cwp) + *cwp = cw; + + return (REVERSE_LIST (ret, WORD_LIST *)); +} +#endif /* READLINE */ + +#if 0 +/* UNUSED */ +/* Extract the name of the variable to bind to from the assignment string. */ +char * +assignment_name (string) + char *string; +{ + int offset; + char *temp; + + offset = assignment (string, 0); + if (offset == 0) + return (char *)NULL; + temp = substring (string, 0, offset); + return (temp); +} +#endif + +/* **************************************************************** */ +/* */ +/* Functions to convert strings to WORD_LISTs and vice versa */ +/* */ +/* **************************************************************** */ + +/* Return a single string of all the words in LIST. SEP is the separator + to put between individual elements of LIST in the output string. */ +char * +string_list_internal (list, sep) + WORD_LIST *list; + char *sep; +{ + register WORD_LIST *t; + char *result, *r; + int word_len, sep_len, result_size; + + if (list == 0) + return ((char *)NULL); + + /* Short-circuit quickly if we don't need to separate anything. */ + if (list->next == 0) + return (savestring (list->word->word)); + + /* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */ + sep_len = STRLEN (sep); + result_size = 0; + + for (t = list; t; t = t->next) + { + if (t != list) + result_size += sep_len; + result_size += strlen (t->word->word); + } + + r = result = (char *)xmalloc (result_size + 1); + + for (t = list; t; t = t->next) + { + if (t != list && sep_len) + { + if (sep_len > 1) + { + FASTCOPY (sep, r, sep_len); + r += sep_len; + } + else + *r++ = sep[0]; + } + + word_len = strlen (t->word->word); + FASTCOPY (t->word->word, r, word_len); + r += word_len; + } + + *r = '\0'; + return (result); +} + +/* Return a single string of all the words present in LIST, separating + each word with a space. */ +char * +string_list (list) + WORD_LIST *list; +{ + return (string_list_internal (list, " ")); +} + +/* An external interface that can be used by the rest of the shell to + obtain a string containing the first character in $IFS. Handles all + the multibyte complications. If LENP is non-null, it is set to the + length of the returned string. */ +char * +ifs_firstchar (lenp) + int *lenp; +{ + char *ret; + int len; + + ret = xmalloc (MB_LEN_MAX + 1); +#if defined (HANDLE_MULTIBYTE) + if (ifs_firstc_len == 1) + { + ret[0] = ifs_firstc[0]; + ret[1] = '\0'; + len = ret[0] ? 1 : 0; + } + else + { + memcpy (ret, ifs_firstc, ifs_firstc_len); + ret[len = ifs_firstc_len] = '\0'; + } +#else + ret[0] = ifs_firstc; + ret[1] = '\0'; + len = ret[0] ? 0 : 1; +#endif + + if (lenp) + *lenp = len; + + return ret; +} + +/* Return a single string of all the words present in LIST, obeying the + quoting rules for "$*", to wit: (P1003.2, draft 11, 3.5.2) "If the + expansion [of $*] appears within a double quoted string, it expands + to a single field with the value of each parameter separated by the + first character of the IFS variable, or by a <space> if IFS is unset." */ +char * +string_list_dollar_star (list) + WORD_LIST *list; +{ + char *ret; +#if defined (HANDLE_MULTIBYTE) +# if defined (__GNUC__) + char sep[MB_CUR_MAX + 1]; +# else + char *sep = 0; +# endif +#else + char sep[2]; +#endif + +#if defined (HANDLE_MULTIBYTE) +# if !defined (__GNUC__) + sep = (char *)xmalloc (MB_CUR_MAX + 1); +# endif /* !__GNUC__ */ + if (ifs_firstc_len == 1) + { + sep[0] = ifs_firstc[0]; + sep[1] = '\0'; + } + else + { + memcpy (sep, ifs_firstc, ifs_firstc_len); + sep[ifs_firstc_len] = '\0'; + } +#else + sep[0] = ifs_firstc; + sep[1] = '\0'; +#endif + + ret = string_list_internal (list, sep); +#if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__) + free (sep); +#endif + return ret; +} + +/* Turn $@ into a string. If (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + is non-zero, the $@ appears within double quotes, and we should quote + the list before converting it into a string. If IFS is unset, and the + word is not quoted, we just need to quote CTLESC and CTLNUL characters + in the words in the list, because the default value of $IFS is + <space><tab><newline>, IFS characters in the words in the list should + also be split. If IFS is null, and the word is not quoted, we need + to quote the words in the list to preserve the positional parameters + exactly. */ +char * +string_list_dollar_at (list, quoted) + WORD_LIST *list; + int quoted; +{ + char *ifs, *ret; +#if defined (HANDLE_MULTIBYTE) +# if defined (__GNUC__) + char sep[MB_CUR_MAX + 1]; +# else + char *sep = 0; +# endif /* !__GNUC__ */ +#else + char sep[2]; +#endif + WORD_LIST *tlist; + + /* XXX this could just be ifs = ifs_value; */ + ifs = ifs_var ? value_cell (ifs_var) : (char *)0; + +#if defined (HANDLE_MULTIBYTE) +# if !defined (__GNUC__) + sep = (char *)xmalloc (MB_CUR_MAX + 1); +# endif /* !__GNUC__ */ + if (ifs && *ifs) + { + if (ifs_firstc_len == 1) + { + sep[0] = ifs_firstc[0]; + sep[1] = '\0'; + } + else + { + memcpy (sep, ifs_firstc, ifs_firstc_len); + sep[ifs_firstc_len] = '\0'; + } + } + else + { + sep[0] = ' '; + sep[1] = '\0'; + } +#else + sep[0] = (ifs == 0 || *ifs == 0) ? ' ' : *ifs; + sep[1] = '\0'; +#endif + + /* XXX -- why call quote_list if ifs == 0? we can get away without doing + it now that quote_escapes quotes spaces */ +#if 0 + tlist = ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (ifs && *ifs == 0)) +#else + tlist = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE)) +#endif + ? quote_list (list) + : list_quote_escapes (list); + + ret = string_list_internal (tlist, sep); +#if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__) + free (sep); +#endif + return ret; +} + +/* Turn the positional paramters into a string, understanding quoting and + the various subtleties of using the first character of $IFS as the + separator. Calls string_list_dollar_at, string_list_dollar_star, and + string_list as appropriate. */ +char * +string_list_pos_params (pchar, list, quoted) + int pchar; + WORD_LIST *list; + int quoted; +{ + char *ret; + WORD_LIST *tlist; + + if (pchar == '*' && (quoted & Q_DOUBLE_QUOTES)) + { + tlist = quote_list (list); + word_list_remove_quoted_nulls (tlist); + ret = string_list_dollar_star (tlist); + } + else if (pchar == '*' && (quoted & Q_HERE_DOCUMENT)) + { + tlist = quote_list (list); + word_list_remove_quoted_nulls (tlist); + ret = string_list (tlist); + } + else if (pchar == '*') + { + /* Even when unquoted, string_list_dollar_star does the right thing + making sure that the first character of $IFS is used as the + separator. */ + ret = string_list_dollar_star (list); + } + else if (pchar == '@' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + /* We use string_list_dollar_at, but only if the string is quoted, since + that quotes the escapes if it's not, which we don't want. We could + use string_list (the old code did), but that doesn't do the right + thing if the first character of $IFS is not a space. We use + string_list_dollar_star if the string is unquoted so we make sure that + the elements of $@ are separated by the first character of $IFS for + later splitting. */ + ret = string_list_dollar_at (list, quoted); + else if (pchar == '@') + ret = string_list_dollar_star (list); + else + ret = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (list) : list); + + return ret; +} + +/* Return the list of words present in STRING. Separate the string into + words at any of the characters found in SEPARATORS. If QUOTED is + non-zero then word in the list will have its quoted flag set, otherwise + the quoted flag is left as make_word () deemed fit. + + This obeys the P1003.2 word splitting semantics. If `separators' is + exactly <space><tab><newline>, then the splitting algorithm is that of + the Bourne shell, which treats any sequence of characters from `separators' + as a delimiter. If IFS is unset, which results in `separators' being set + to "", no splitting occurs. If separators has some other value, the + following rules are applied (`IFS white space' means zero or more + occurrences of <space>, <tab>, or <newline>, as long as those characters + are in `separators'): + + 1) IFS white space is ignored at the start and the end of the + string. + 2) Each occurrence of a character in `separators' that is not + IFS white space, along with any adjacent occurrences of + IFS white space delimits a field. + 3) Any nonzero-length sequence of IFS white space delimits a field. + */ + +/* BEWARE! list_string strips null arguments. Don't call it twice and + expect to have "" preserved! */ + +/* This performs word splitting and quoted null character removal on + STRING. */ +#define issep(c) \ + (((separators)[0]) ? ((separators)[1] ? isifs(c) \ + : (c) == (separators)[0]) \ + : 0) + +WORD_LIST * +list_string (string, separators, quoted) + register char *string, *separators; + int quoted; +{ + WORD_LIST *result; + WORD_DESC *t; + char *current_word, *s; + int sindex, sh_style_split, whitesep, xflags; + size_t slen; + + if (!string || !*string) + return ((WORD_LIST *)NULL); + + sh_style_split = separators && separators[0] == ' ' && + separators[1] == '\t' && + separators[2] == '\n' && + separators[3] == '\0'; + for (xflags = 0, s = ifs_value; s && *s; s++) + { + if (*s == CTLESC) xflags |= SX_NOCTLESC; + else if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL; + } + + slen = 0; + /* Remove sequences of whitespace at the beginning of STRING, as + long as those characters appear in IFS. Do not do this if + STRING is quoted or if there are no separator characters. */ + if (!quoted || !separators || !*separators) + { + for (s = string; *s && spctabnl (*s) && issep (*s); s++); + + if (!*s) + return ((WORD_LIST *)NULL); + + string = s; + } + + /* OK, now STRING points to a word that does not begin with white space. + The splitting algorithm is: + extract a word, stopping at a separator + skip sequences of spc, tab, or nl as long as they are separators + This obeys the field splitting rules in Posix.2. */ + slen = (MB_CUR_MAX > 1) ? strlen (string) : 1; + for (result = (WORD_LIST *)NULL, sindex = 0; string[sindex]; ) + { + /* Don't need string length in ADVANCE_CHAR or string_extract_verbatim + unless multibyte chars are possible. */ + current_word = string_extract_verbatim (string, slen, &sindex, separators, xflags); + if (current_word == 0) + break; + + /* If we have a quoted empty string, add a quoted null argument. We + want to preserve the quoted null character iff this is a quoted + empty string; otherwise the quoted null characters are removed + below. */ + if (QUOTED_NULL (current_word)) + { + t = alloc_word_desc (); + t->word = make_quoted_char ('\0'); + t->flags |= W_QUOTED|W_HASQUOTEDNULL; + result = make_word_list (t, result); + } + else if (current_word[0] != '\0') + { + /* If we have something, then add it regardless. However, + perform quoted null character removal on the current word. */ + remove_quoted_nulls (current_word); + result = add_string_to_list (current_word, result); + result->word->flags &= ~W_HASQUOTEDNULL; /* just to be sure */ + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + result->word->flags |= W_QUOTED; + } + + /* If we're not doing sequences of separators in the traditional + Bourne shell style, then add a quoted null argument. */ + else if (!sh_style_split && !spctabnl (string[sindex])) + { + t = alloc_word_desc (); + t->word = make_quoted_char ('\0'); + t->flags |= W_QUOTED|W_HASQUOTEDNULL; + result = make_word_list (t, result); + } + + free (current_word); + + /* Note whether or not the separator is IFS whitespace, used later. */ + whitesep = string[sindex] && spctabnl (string[sindex]); + + /* Move past the current separator character. */ + if (string[sindex]) + { + DECLARE_MBSTATE; + ADVANCE_CHAR (string, slen, sindex); + } + + /* Now skip sequences of space, tab, or newline characters if they are + in the list of separators. */ + while (string[sindex] && spctabnl (string[sindex]) && issep (string[sindex])) + sindex++; + + /* If the first separator was IFS whitespace and the current character + is a non-whitespace IFS character, it should be part of the current + field delimiter, not a separate delimiter that would result in an + empty field. Look at POSIX.2, 3.6.5, (3)(b). */ + if (string[sindex] && whitesep && issep (string[sindex]) && !spctabnl (string[sindex])) + { + sindex++; + /* An IFS character that is not IFS white space, along with any + adjacent IFS white space, shall delimit a field. (SUSv3) */ + while (string[sindex] && spctabnl (string[sindex]) && isifs (string[sindex])) + sindex++; + } + } + return (REVERSE_LIST (result, WORD_LIST *)); +} + +/* Parse a single word from STRING, using SEPARATORS to separate fields. + ENDPTR is set to the first character after the word. This is used by + the `read' builtin. This is never called with SEPARATORS != $IFS; + it should be simplified. + + XXX - this function is very similar to list_string; they should be + combined - XXX */ +char * +get_word_from_string (stringp, separators, endptr) + char **stringp, *separators, **endptr; +{ + register char *s; + char *current_word; + int sindex, sh_style_split, whitesep, xflags; + size_t slen; + + if (!stringp || !*stringp || !**stringp) + return ((char *)NULL); + + sh_style_split = separators && separators[0] == ' ' && + separators[1] == '\t' && + separators[2] == '\n' && + separators[3] == '\0'; + for (xflags = 0, s = ifs_value; s && *s; s++) + { + if (*s == CTLESC) xflags |= SX_NOCTLESC; + if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL; + } + + s = *stringp; + slen = 0; + + /* Remove sequences of whitespace at the beginning of STRING, as + long as those characters appear in IFS. */ + if (sh_style_split || !separators || !*separators) + { + for (; *s && spctabnl (*s) && isifs (*s); s++); + + /* If the string is nothing but whitespace, update it and return. */ + if (!*s) + { + *stringp = s; + if (endptr) + *endptr = s; + return ((char *)NULL); + } + } + + /* OK, S points to a word that does not begin with white space. + Now extract a word, stopping at a separator, save a pointer to + the first character after the word, then skip sequences of spc, + tab, or nl as long as they are separators. + + This obeys the field splitting rules in Posix.2. */ + sindex = 0; + /* Don't need string length in ADVANCE_CHAR or string_extract_verbatim + unless multibyte chars are possible. */ + slen = (MB_CUR_MAX > 1) ? strlen (s) : 1; + current_word = string_extract_verbatim (s, slen, &sindex, separators, xflags); + + /* Set ENDPTR to the first character after the end of the word. */ + if (endptr) + *endptr = s + sindex; + + /* Note whether or not the separator is IFS whitespace, used later. */ + whitesep = s[sindex] && spctabnl (s[sindex]); + + /* Move past the current separator character. */ + if (s[sindex]) + { + DECLARE_MBSTATE; + ADVANCE_CHAR (s, slen, sindex); + } + + /* Now skip sequences of space, tab, or newline characters if they are + in the list of separators. */ + while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex])) + sindex++; + + /* If the first separator was IFS whitespace and the current character is + a non-whitespace IFS character, it should be part of the current field + delimiter, not a separate delimiter that would result in an empty field. + Look at POSIX.2, 3.6.5, (3)(b). */ + if (s[sindex] && whitesep && isifs (s[sindex]) && !spctabnl (s[sindex])) + { + sindex++; + /* An IFS character that is not IFS white space, along with any adjacent + IFS white space, shall delimit a field. */ + while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex])) + sindex++; + } + + /* Update STRING to point to the next field. */ + *stringp = s + sindex; + return (current_word); +} + +/* Remove IFS white space at the end of STRING. Start at the end + of the string and walk backwards until the beginning of the string + or we find a character that's not IFS white space and not CTLESC. + Only let CTLESC escape a white space character if SAW_ESCAPE is + non-zero. */ +char * +strip_trailing_ifs_whitespace (string, separators, saw_escape) + char *string, *separators; + int saw_escape; +{ + char *s; + + s = string + STRLEN (string) - 1; + while (s > string && ((spctabnl (*s) && isifs (*s)) || + (saw_escape && *s == CTLESC && spctabnl (s[1])))) + s--; + *++s = '\0'; + return string; +} + +#if 0 +/* UNUSED */ +/* Split STRING into words at whitespace. Obeys shell-style quoting with + backslashes, single and double quotes. */ +WORD_LIST * +list_string_with_quotes (string) + char *string; +{ + WORD_LIST *list; + char *token, *s; + size_t s_len; + int c, i, tokstart, len; + + for (s = string; s && *s && spctabnl (*s); s++) + ; + if (s == 0 || *s == 0) + return ((WORD_LIST *)NULL); + + s_len = strlen (s); + tokstart = i = 0; + list = (WORD_LIST *)NULL; + while (1) + { + c = s[i]; + if (c == '\\') + { + i++; + if (s[i]) + i++; + } + else if (c == '\'') + i = skip_single_quoted (s, s_len, ++i); + else if (c == '"') + i = skip_double_quoted (s, s_len, ++i); + else if (c == 0 || spctabnl (c)) + { + /* We have found the end of a token. Make a word out of it and + add it to the word list. */ + token = substring (s, tokstart, i); + list = add_string_to_list (token, list); + free (token); + while (spctabnl (s[i])) + i++; + if (s[i]) + tokstart = i; + else + break; + } + else + i++; /* normal character */ + } + return (REVERSE_LIST (list, WORD_LIST *)); +} +#endif + +/********************************************************/ +/* */ +/* Functions to perform assignment statements */ +/* */ +/********************************************************/ + +#if defined (ARRAY_VARS) +static SHELL_VAR * +do_compound_assignment (name, value, flags) + char *name, *value; + int flags; +{ + SHELL_VAR *v; + int mklocal, mkassoc; + WORD_LIST *list; + + mklocal = flags & ASS_MKLOCAL; + mkassoc = flags & ASS_MKASSOC; + + if (mklocal && variable_context) + { + v = find_variable (name); + list = expand_compound_array_assignment (v, value, flags); + if (mkassoc) + v = make_local_assoc_variable (name); + else if (v == 0 || (array_p (v) == 0 && assoc_p (v) == 0) || v->context != variable_context) + v = make_local_array_variable (name); + assign_compound_array_list (v, list, flags); + } + else + v = assign_array_from_string (name, value, flags); + + return (v); +} +#endif + +/* Given STRING, an assignment string, get the value of the right side + of the `=', and bind it to the left side. If EXPAND is true, then + perform parameter expansion, command substitution, and arithmetic + expansion on the right-hand side. Perform tilde expansion in any + case. Do not perform word splitting on the result of expansion. */ +static int +do_assignment_internal (word, expand) + const WORD_DESC *word; + int expand; +{ + int offset, appendop, assign_list, aflags, retval; + char *name, *value, *temp; + SHELL_VAR *entry; +#if defined (ARRAY_VARS) + char *t; + int ni; +#endif + const char *string; + + if (word == 0 || word->word == 0) + return 0; + + appendop = assign_list = aflags = 0; + string = word->word; + offset = assignment (string, 0); + name = savestring (string); + value = (char *)NULL; + + if (name[offset] == '=') + { + if (name[offset - 1] == '+') + { + appendop = 1; + name[offset - 1] = '\0'; + } + + name[offset] = 0; /* might need this set later */ + temp = name + offset + 1; + +#if defined (ARRAY_VARS) + if (expand && (word->flags & W_COMPASSIGN)) + { + assign_list = ni = 1; + value = extract_array_assignment_list (temp, &ni); + } + else +#endif + if (expand && temp[0]) + value = expand_string_if_necessary (temp, 0, expand_string_assignment); + else + value = savestring (temp); + } + + if (value == 0) + { + value = (char *)xmalloc (1); + value[0] = '\0'; + } + + if (echo_command_at_execute) + { + if (appendop) + name[offset - 1] = '+'; + xtrace_print_assignment (name, value, assign_list, 1); + if (appendop) + name[offset - 1] = '\0'; + } + +#define ASSIGN_RETURN(r) do { FREE (value); free (name); return (r); } while (0) + + if (appendop) + aflags |= ASS_APPEND; + +#if defined (ARRAY_VARS) + if (t = mbschr (name, '[')) /*]*/ + { + if (assign_list) + { + report_error (_("%s: cannot assign list to array member"), name); + ASSIGN_RETURN (0); + } + entry = assign_array_element (name, value, aflags); + if (entry == 0) + ASSIGN_RETURN (0); + } + else if (assign_list) + { + if (word->flags & W_ASSIGNARG) + aflags |= ASS_MKLOCAL; + if (word->flags & W_ASSIGNASSOC) + aflags |= ASS_MKASSOC; + entry = do_compound_assignment (name, value, aflags); + } + else +#endif /* ARRAY_VARS */ + entry = bind_variable (name, value, aflags); + + stupidly_hack_special_variables (name); + +#if 1 + /* Return 1 if the assignment seems to have been performed correctly. */ + if (entry == 0 || readonly_p (entry)) + retval = 0; /* assignment failure */ + else if (noassign_p (entry)) + { + last_command_exit_value = EXECUTION_FAILURE; + retval = 1; /* error status, but not assignment failure */ + } + else + retval = 1; + + if (entry && retval != 0 && noassign_p (entry) == 0) + VUNSETATTR (entry, att_invisible); + + ASSIGN_RETURN (retval); +#else + if (entry) + VUNSETATTR (entry, att_invisible); + + ASSIGN_RETURN (entry ? ((readonly_p (entry) == 0) && noassign_p (entry) == 0) : 0); +#endif +} + +/* Perform the assignment statement in STRING, and expand the + right side by doing tilde, command and parameter expansion. */ +int +do_assignment (string) + char *string; +{ + WORD_DESC td; + + td.flags = W_ASSIGNMENT; + td.word = string; + + return do_assignment_internal (&td, 1); +} + +int +do_word_assignment (word) + WORD_DESC *word; +{ + return do_assignment_internal (word, 1); +} + +/* Given STRING, an assignment string, get the value of the right side + of the `=', and bind it to the left side. Do not perform any word + expansions on the right hand side. */ +int +do_assignment_no_expand (string) + char *string; +{ + WORD_DESC td; + + td.flags = W_ASSIGNMENT; + td.word = string; + + return (do_assignment_internal (&td, 0)); +} + +/*************************************************** + * * + * Functions to manage the positional parameters * + * * + ***************************************************/ + +/* Return the word list that corresponds to `$*'. */ +WORD_LIST * +list_rest_of_args () +{ + register WORD_LIST *list, *args; + int i; + + /* Break out of the loop as soon as one of the dollar variables is null. */ + for (i = 1, list = (WORD_LIST *)NULL; i < 10 && dollar_vars[i]; i++) + list = make_word_list (make_bare_word (dollar_vars[i]), list); + + for (args = rest_of_args; args; args = args->next) + list = make_word_list (make_bare_word (args->word->word), list); + + return (REVERSE_LIST (list, WORD_LIST *)); +} + +int +number_of_args () +{ + register WORD_LIST *list; + int n; + + for (n = 0; n < 9 && dollar_vars[n+1]; n++) + ; + for (list = rest_of_args; list; list = list->next) + n++; + return n; +} + +/* Return the value of a positional parameter. This handles values > 10. */ +char * +get_dollar_var_value (ind) + intmax_t ind; +{ + char *temp; + WORD_LIST *p; + + if (ind < 10) + temp = dollar_vars[ind] ? savestring (dollar_vars[ind]) : (char *)NULL; + else /* We want something like ${11} */ + { + ind -= 10; + for (p = rest_of_args; p && ind--; p = p->next) + ; + temp = p ? savestring (p->word->word) : (char *)NULL; + } + return (temp); +} + +/* Make a single large string out of the dollar digit variables, + and the rest_of_args. If DOLLAR_STAR is 1, then obey the special + case of "$*" with respect to IFS. */ +char * +string_rest_of_args (dollar_star) + int dollar_star; +{ + register WORD_LIST *list; + char *string; + + list = list_rest_of_args (); + string = dollar_star ? string_list_dollar_star (list) : string_list (list); + dispose_words (list); + return (string); +} + +/* Return a string containing the positional parameters from START to + END, inclusive. If STRING[0] == '*', we obey the rules for $*, + which only makes a difference if QUOTED is non-zero. If QUOTED includes + Q_HERE_DOCUMENT or Q_DOUBLE_QUOTES, this returns a quoted list, otherwise + no quoting chars are added. */ +static char * +pos_params (string, start, end, quoted) + char *string; + int start, end, quoted; +{ + WORD_LIST *save, *params, *h, *t; + char *ret; + int i; + + /* see if we can short-circuit. if start == end, we want 0 parameters. */ + if (start == end) + return ((char *)NULL); + + save = params = list_rest_of_args (); + if (save == 0) + return ((char *)NULL); + + if (start == 0) /* handle ${@:0[:x]} specially */ + { + t = make_word_list (make_word (dollar_vars[0]), params); + save = params = t; + } + + for (i = start ? 1 : 0; params && i < start; i++) + params = params->next; + if (params == 0) + return ((char *)NULL); + for (h = t = params; params && i < end; i++) + { + t = params; + params = params->next; + } + + t->next = (WORD_LIST *)NULL; + + ret = string_list_pos_params (string[0], h, quoted); + + if (t != params) + t->next = params; + + dispose_words (save); + return (ret); +} + +/******************************************************************/ +/* */ +/* Functions to expand strings to strings or WORD_LISTs */ +/* */ +/******************************************************************/ + +#if defined (PROCESS_SUBSTITUTION) +#define EXP_CHAR(s) (s == '$' || s == '`' || s == '<' || s == '>' || s == CTLESC || s == '~') +#else +#define EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC || s == '~') +#endif + +/* If there are any characters in STRING that require full expansion, + then call FUNC to expand STRING; otherwise just perform quote + removal if necessary. This returns a new string. */ +static char * +expand_string_if_necessary (string, quoted, func) + char *string; + int quoted; + EXPFUNC *func; +{ + WORD_LIST *list; + size_t slen; + int i, saw_quote; + char *ret; + DECLARE_MBSTATE; + + /* Don't need string length for ADVANCE_CHAR unless multibyte chars possible. */ + slen = (MB_CUR_MAX > 1) ? strlen (string) : 0; + i = saw_quote = 0; + while (string[i]) + { + if (EXP_CHAR (string[i])) + break; + else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"') + saw_quote = 1; + ADVANCE_CHAR (string, slen, i); + } + + if (string[i]) + { + list = (*func) (string, quoted); + if (list) + { + ret = string_list (list); + dispose_words (list); + } + else + ret = (char *)NULL; + } + else if (saw_quote && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)) + ret = string_quote_removal (string, quoted); + else + ret = savestring (string); + + return ret; +} + +static inline char * +expand_string_to_string_internal (string, quoted, func) + char *string; + int quoted; + EXPFUNC *func; +{ + WORD_LIST *list; + char *ret; + + if (string == 0 || *string == '\0') + return ((char *)NULL); + + list = (*func) (string, quoted); + if (list) + { + ret = string_list (list); + dispose_words (list); + } + else + ret = (char *)NULL; + + return (ret); +} + +char * +expand_string_to_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_to_string_internal (string, quoted, expand_string)); +} + +char * +expand_string_unsplit_to_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_to_string_internal (string, quoted, expand_string_unsplit)); +} + +char * +expand_assignment_string_to_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_to_string_internal (string, quoted, expand_string_assignment)); +} + +char * +expand_arith_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_if_necessary (string, quoted, expand_string)); +} + +#if defined (COND_COMMAND) +/* Just remove backslashes in STRING. Returns a new string. */ +char * +remove_backslashes (string) + char *string; +{ + char *r, *ret, *s; + + r = ret = (char *)xmalloc (strlen (string) + 1); + for (s = string; s && *s; ) + { + if (*s == '\\') + s++; + if (*s == 0) + break; + *r++ = *s++; + } + *r = '\0'; + return ret; +} + +/* This needs better error handling. */ +/* Expand W for use as an argument to a unary or binary operator in a + [[...]] expression. If SPECIAL is 1, this is the rhs argument + to the != or == operator, and should be treated as a pattern. In + this case, we quote the string specially for the globbing code. If + SPECIAL is 2, this is an rhs argument for the =~ operator, and should + be quoted appropriately for regcomp/regexec. The caller is responsible + for removing the backslashes if the unquoted word is needed later. */ +char * +cond_expand_word (w, special) + WORD_DESC *w; + int special; +{ + char *r, *p; + WORD_LIST *l; + int qflags; + + if (w->word == 0 || w->word[0] == '\0') + return ((char *)NULL); + + w->flags |= W_NOSPLIT2; + l = call_expand_word_internal (w, 0, 0, (int *)0, (int *)0); + if (l) + { + if (special == 0) + { + dequote_list (l); + r = string_list (l); + } + else + { + qflags = QGLOB_CVTNULL; + if (special == 2) + qflags |= QGLOB_REGEXP; + p = string_list (l); + r = quote_string_for_globbing (p, qflags); + free (p); + } + dispose_words (l); + } + else + r = (char *)NULL; + + return r; +} +#endif + +/* Call expand_word_internal to expand W and handle error returns. + A convenience function for functions that don't want to handle + any errors or free any memory before aborting. */ +static WORD_LIST * +call_expand_word_internal (w, q, i, c, e) + WORD_DESC *w; + int q, i, *c, *e; +{ + WORD_LIST *result; + + result = expand_word_internal (w, q, i, c, e); + if (result == &expand_word_error || result == &expand_word_fatal) + { + /* By convention, each time this error is returned, w->word has + already been freed (it sometimes may not be in the fatal case, + but that doesn't result in a memory leak because we're going + to exit in most cases). */ + w->word = (char *)NULL; + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level ((result == &expand_word_error) ? DISCARD : FORCE_EOF); + /* NOTREACHED */ + } + else + return (result); +} + +/* Perform parameter expansion, command substitution, and arithmetic + expansion on STRING, as if it were a word. Leave the result quoted. */ +static WORD_LIST * +expand_string_internal (string, quoted) + char *string; + int quoted; +{ + WORD_DESC td; + WORD_LIST *tresult; + + if (string == 0 || *string == 0) + return ((WORD_LIST *)NULL); + + td.flags = 0; + td.word = savestring (string); + + tresult = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL); + + FREE (td.word); + return (tresult); +} + +/* Expand STRING by performing parameter expansion, command substitution, + and arithmetic expansion. Dequote the resulting WORD_LIST before + returning it, but do not perform word splitting. The call to + remove_quoted_nulls () is in here because word splitting normally + takes care of quote removal. */ +WORD_LIST * +expand_string_unsplit (string, quoted) + char *string; + int quoted; +{ + WORD_LIST *value; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + expand_no_split_dollar_star = 1; + value = expand_string_internal (string, quoted); + expand_no_split_dollar_star = 0; + + if (value) + { + if (value->word) + { + remove_quoted_nulls (value->word->word); + value->word->flags &= ~W_HASQUOTEDNULL; + } + dequote_list (value); + } + return (value); +} + +/* Expand the rhs of an assignment statement */ +WORD_LIST * +expand_string_assignment (string, quoted) + char *string; + int quoted; +{ + WORD_DESC td; + WORD_LIST *value; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + expand_no_split_dollar_star = 1; + + td.flags = W_ASSIGNRHS; + td.word = savestring (string); + value = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL); + FREE (td.word); + + expand_no_split_dollar_star = 0; + + if (value) + { + if (value->word) + { + remove_quoted_nulls (value->word->word); + value->word->flags &= ~W_HASQUOTEDNULL; + } + dequote_list (value); + } + return (value); +} + + +/* Expand one of the PS? prompt strings. This is a sort of combination of + expand_string_unsplit and expand_string_internal, but returns the + passed string when an error occurs. Might want to trap other calls + to jump_to_top_level here so we don't endlessly loop. */ +WORD_LIST * +expand_prompt_string (string, quoted, wflags) + char *string; + int quoted; + int wflags; +{ + WORD_LIST *value; + WORD_DESC td; + + if (string == 0 || *string == 0) + return ((WORD_LIST *)NULL); + + td.flags = wflags; + td.word = savestring (string); + + no_longjmp_on_fatal_error = 1; + value = expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL); + no_longjmp_on_fatal_error = 0; + + if (value == &expand_word_error || value == &expand_word_fatal) + { + value = make_word_list (make_bare_word (string), (WORD_LIST *)NULL); + return value; + } + FREE (td.word); + if (value) + { + if (value->word) + { + remove_quoted_nulls (value->word->word); + value->word->flags &= ~W_HASQUOTEDNULL; + } + dequote_list (value); + } + return (value); +} + +/* Expand STRING just as if you were expanding a word, but do not dequote + the resultant WORD_LIST. This is called only from within this file, + and is used to correctly preserve quoted characters when expanding + things like ${1+"$@"}. This does parameter expansion, command + substitution, arithmetic expansion, and word splitting. */ +static WORD_LIST * +expand_string_leave_quoted (string, quoted) + char *string; + int quoted; +{ + WORD_LIST *tlist; + WORD_LIST *tresult; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + tlist = expand_string_internal (string, quoted); + + if (tlist) + { + tresult = word_list_split (tlist); + dispose_words (tlist); + return (tresult); + } + return ((WORD_LIST *)NULL); +} + +/* This does not perform word splitting or dequote the WORD_LIST + it returns. */ +static WORD_LIST * +expand_string_for_rhs (string, quoted, dollar_at_p, has_dollar_at) + char *string; + int quoted, *dollar_at_p, *has_dollar_at; +{ + WORD_DESC td; + WORD_LIST *tresult; + + if (string == 0 || *string == '\0') + return (WORD_LIST *)NULL; + + td.flags = 0; + td.word = string; + tresult = call_expand_word_internal (&td, quoted, 1, dollar_at_p, has_dollar_at); + return (tresult); +} + +/* Expand STRING just as if you were expanding a word. This also returns + a list of words. Note that filename globbing is *NOT* done for word + or string expansion, just when the shell is expanding a command. This + does parameter expansion, command substitution, arithmetic expansion, + and word splitting. Dequote the resultant WORD_LIST before returning. */ +WORD_LIST * +expand_string (string, quoted) + char *string; + int quoted; +{ + WORD_LIST *result; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + result = expand_string_leave_quoted (string, quoted); + return (result ? dequote_list (result) : result); +} + +/*************************************************** + * * + * Functions to handle quoting chars * + * * + ***************************************************/ + +/* Conventions: + + A string with s[0] == CTLNUL && s[1] == 0 is a quoted null string. + The parser passes CTLNUL as CTLESC CTLNUL. */ + +/* Quote escape characters in string s, but no other characters. This is + used to protect CTLESC and CTLNUL in variable values from the rest of + the word expansion process after the variable is expanded (word splitting + and filename generation). If IFS is null, we quote spaces as well, just + in case we split on spaces later (in the case of unquoted $@, we will + eventually attempt to split the entire word on spaces). Corresponding + code exists in dequote_escapes. Even if we don't end up splitting on + spaces, quoting spaces is not a problem. This should never be called on + a string that is quoted with single or double quotes or part of a here + document (effectively double-quoted). */ +char * +quote_escapes (string) + char *string; +{ + register char *s, *t; + size_t slen; + char *result, *send; + int quote_spaces, skip_ctlesc, skip_ctlnul; + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + + quote_spaces = (ifs_value && *ifs_value == 0); + + for (skip_ctlesc = skip_ctlnul = 0, s = ifs_value; s && *s; s++) + skip_ctlesc |= *s == CTLESC, skip_ctlnul |= *s == CTLNUL; + + t = result = (char *)xmalloc ((slen * 2) + 1); + s = string; + + while (*s) + { + if ((skip_ctlesc == 0 && *s == CTLESC) || (skip_ctlnul == 0 && *s == CTLNUL) || (quote_spaces && *s == ' ')) + *t++ = CTLESC; + COPY_CHAR_P (t, s, send); + } + *t = '\0'; + return (result); +} + +static WORD_LIST * +list_quote_escapes (list) + WORD_LIST *list; +{ + register WORD_LIST *w; + char *t; + + for (w = list; w; w = w->next) + { + t = w->word->word; + w->word->word = quote_escapes (t); + free (t); + } + return list; +} + +/* Inverse of quote_escapes; remove CTLESC protecting CTLESC or CTLNUL. + + The parser passes us CTLESC as CTLESC CTLESC and CTLNUL as CTLESC CTLNUL. + This is necessary to make unquoted CTLESC and CTLNUL characters in the + data stream pass through properly. + + We need to remove doubled CTLESC characters inside quoted strings before + quoting the entire string, so we do not double the number of CTLESC + characters. + + Also used by parts of the pattern substitution code. */ +char * +dequote_escapes (string) + char *string; +{ + register char *s, *t, *s1; + size_t slen; + char *result, *send; + int quote_spaces; + DECLARE_MBSTATE; + + if (string == 0) + return string; + + slen = strlen (string); + send = string + slen; + + t = result = (char *)xmalloc (slen + 1); + + if (strchr (string, CTLESC) == 0) + return (strcpy (result, string)); + + quote_spaces = (ifs_value && *ifs_value == 0); + + s = string; + while (*s) + { + if (*s == CTLESC && (s[1] == CTLESC || s[1] == CTLNUL || (quote_spaces && s[1] == ' '))) + { + s++; + if (*s == '\0') + break; + } + COPY_CHAR_P (t, s, send); + } + *t = '\0'; + return result; +} + +/* Return a new string with the quoted representation of character C. + This turns "" into QUOTED_NULL, so the W_HASQUOTEDNULL flag needs to be + set in any resultant WORD_DESC where this value is the word. */ +static char * +make_quoted_char (c) + int c; +{ + char *temp; + + temp = (char *)xmalloc (3); + if (c == 0) + { + temp[0] = CTLNUL; + temp[1] = '\0'; + } + else + { + temp[0] = CTLESC; + temp[1] = c; + temp[2] = '\0'; + } + return (temp); +} + +/* Quote STRING, returning a new string. This turns "" into QUOTED_NULL, so + the W_HASQUOTEDNULL flag needs to be set in any resultant WORD_DESC where + this value is the word. */ +char * +quote_string (string) + char *string; +{ + register char *t; + size_t slen; + char *result, *send; + + if (*string == 0) + { + result = (char *)xmalloc (2); + result[0] = CTLNUL; + result[1] = '\0'; + } + else + { + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + + result = (char *)xmalloc ((slen * 2) + 1); + + for (t = result; string < send; ) + { + *t++ = CTLESC; + COPY_CHAR_P (t, string, send); + } + *t = '\0'; + } + return (result); +} + +/* De-quote quoted characters in STRING. */ +char * +dequote_string (string) + char *string; +{ + register char *s, *t; + size_t slen; + char *result, *send; + DECLARE_MBSTATE; + + slen = strlen (string); + + t = result = (char *)xmalloc (slen + 1); + + if (QUOTED_NULL (string)) + { + result[0] = '\0'; + return (result); + } + + /* If no character in the string can be quoted, don't bother examining + each character. Just return a copy of the string passed to us. */ + if (strchr (string, CTLESC) == NULL) + return (strcpy (result, string)); + + send = string + slen; + s = string; + while (*s) + { + if (*s == CTLESC) + { + s++; + if (*s == '\0') + break; + } + COPY_CHAR_P (t, s, send); + } + + *t = '\0'; + return (result); +} + +/* Quote the entire WORD_LIST list. */ +static WORD_LIST * +quote_list (list) + WORD_LIST *list; +{ + register WORD_LIST *w; + char *t; + + for (w = list; w; w = w->next) + { + t = w->word->word; + w->word->word = quote_string (t); + if (*t == 0) + w->word->flags |= W_HASQUOTEDNULL; /* XXX - turn on W_HASQUOTEDNULL here? */ + w->word->flags |= W_QUOTED; + free (t); + } + return list; +} + +/* De-quote quoted characters in each word in LIST. */ +WORD_LIST * +dequote_list (list) + WORD_LIST *list; +{ + register char *s; + register WORD_LIST *tlist; + + for (tlist = list; tlist; tlist = tlist->next) + { + s = dequote_string (tlist->word->word); + if (QUOTED_NULL (tlist->word->word)) + tlist->word->flags &= ~W_HASQUOTEDNULL; + free (tlist->word->word); + tlist->word->word = s; + } + return list; +} + +/* Remove CTLESC protecting a CTLESC or CTLNUL in place. Return the passed + string. */ +char * +remove_quoted_escapes (string) + char *string; +{ + char *t; + + if (string) + { + t = dequote_escapes (string); + strcpy (string, t); + free (t); + } + + return (string); +} + +/* Perform quoted null character removal on STRING. We don't allow any + quoted null characters in the middle or at the ends of strings because + of how expand_word_internal works. remove_quoted_nulls () turns + STRING into an empty string iff it only consists of a quoted null, + and removes all unquoted CTLNUL characters. */ +char * +remove_quoted_nulls (string) + char *string; +{ + register size_t slen; + register int i, j, prev_i; + DECLARE_MBSTATE; + + if (strchr (string, CTLNUL) == 0) /* XXX */ + return string; /* XXX */ + + slen = strlen (string); + i = j = 0; + + while (i < slen) + { + if (string[i] == CTLESC) + { + /* Old code had j++, but we cannot assume that i == j at this + point -- what if a CTLNUL has already been removed from the + string? We don't want to drop the CTLESC or recopy characters + that we've already copied down. */ + i++; string[j++] = CTLESC; + if (i == slen) + break; + } + else if (string[i] == CTLNUL) + i++; + + prev_i = i; + ADVANCE_CHAR (string, slen, i); + if (j < prev_i) + { + do string[j++] = string[prev_i++]; while (prev_i < i); + } + else + j = i; + } + string[j] = '\0'; + + return (string); +} + +/* Perform quoted null character removal on each element of LIST. + This modifies LIST. */ +void +word_list_remove_quoted_nulls (list) + WORD_LIST *list; +{ + register WORD_LIST *t; + + for (t = list; t; t = t->next) + { + remove_quoted_nulls (t->word->word); + t->word->flags &= ~W_HASQUOTEDNULL; + } +} + +/* **************************************************************** */ +/* */ +/* Functions for Matching and Removing Patterns */ +/* */ +/* **************************************************************** */ + +#if defined (HANDLE_MULTIBYTE) +#if 0 /* Currently unused */ +static unsigned char * +mb_getcharlens (string, len) + char *string; + int len; +{ + int i, offset, last; + unsigned char *ret; + char *p; + DECLARE_MBSTATE; + + i = offset = 0; + last = 0; + ret = (unsigned char *)xmalloc (len); + memset (ret, 0, len); + while (string[last]) + { + ADVANCE_CHAR (string, len, offset); + ret[last] = offset - last; + last = offset; + } + return ret; +} +#endif +#endif + +/* Remove the portion of PARAM matched by PATTERN according to OP, where OP + can have one of 4 values: + RP_LONG_LEFT remove longest matching portion at start of PARAM + RP_SHORT_LEFT remove shortest matching portion at start of PARAM + RP_LONG_RIGHT remove longest matching portion at end of PARAM + RP_SHORT_RIGHT remove shortest matching portion at end of PARAM +*/ + +#define RP_LONG_LEFT 1 +#define RP_SHORT_LEFT 2 +#define RP_LONG_RIGHT 3 +#define RP_SHORT_RIGHT 4 + +/* Returns its first argument if nothing matched; new memory otherwise */ +static char * +remove_upattern (param, pattern, op) + char *param, *pattern; + int op; +{ + register int len; + register char *end; + register char *p, *ret, c; + + len = STRLEN (param); + end = param + len; + + switch (op) + { + case RP_LONG_LEFT: /* remove longest match at start */ + for (p = end; p >= param; p--) + { + c = *p; *p = '\0'; + if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + *p = c; + return (savestring (p)); + } + *p = c; + + } + break; + + case RP_SHORT_LEFT: /* remove shortest match at start */ + for (p = param; p <= end; p++) + { + c = *p; *p = '\0'; + if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + *p = c; + return (savestring (p)); + } + *p = c; + } + break; + + case RP_LONG_RIGHT: /* remove longest match at end */ + for (p = param; p <= end; p++) + { + if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + c = *p; *p = '\0'; + ret = savestring (param); + *p = c; + return (ret); + } + } + break; + + case RP_SHORT_RIGHT: /* remove shortest match at end */ + for (p = end; p >= param; p--) + { + if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + c = *p; *p = '\0'; + ret = savestring (param); + *p = c; + return (ret); + } + } + break; + } + + return (param); /* no match, return original string */ +} + +#if defined (HANDLE_MULTIBYTE) +/* Returns its first argument if nothing matched; new memory otherwise */ +static wchar_t * +remove_wpattern (wparam, wstrlen, wpattern, op) + wchar_t *wparam; + size_t wstrlen; + wchar_t *wpattern; + int op; +{ + wchar_t wc, *ret; + int n; + + switch (op) + { + case RP_LONG_LEFT: /* remove longest match at start */ + for (n = wstrlen; n >= 0; n--) + { + wc = wparam[n]; wparam[n] = L'\0'; + if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wparam[n] = wc; + return (wcsdup (wparam + n)); + } + wparam[n] = wc; + } + break; + + case RP_SHORT_LEFT: /* remove shortest match at start */ + for (n = 0; n <= wstrlen; n++) + { + wc = wparam[n]; wparam[n] = L'\0'; + if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wparam[n] = wc; + return (wcsdup (wparam + n)); + } + wparam[n] = wc; + } + break; + + case RP_LONG_RIGHT: /* remove longest match at end */ + for (n = 0; n <= wstrlen; n++) + { + if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wc = wparam[n]; wparam[n] = L'\0'; + ret = wcsdup (wparam); + wparam[n] = wc; + return (ret); + } + } + break; + + case RP_SHORT_RIGHT: /* remove shortest match at end */ + for (n = wstrlen; n >= 0; n--) + { + if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wc = wparam[n]; wparam[n] = L'\0'; + ret = wcsdup (wparam); + wparam[n] = wc; + return (ret); + } + } + break; + } + + return (wparam); /* no match, return original string */ +} +#endif /* HANDLE_MULTIBYTE */ + +static char * +remove_pattern (param, pattern, op) + char *param, *pattern; + int op; +{ + char *xret; + + if (param == NULL) + return (param); + if (*param == '\0' || pattern == NULL || *pattern == '\0') /* minor optimization */ + return (savestring (param)); + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + { + wchar_t *ret, *oret; + size_t n; + wchar_t *wparam, *wpattern; + mbstate_t ps; + + n = xdupmbstowcs (&wpattern, NULL, pattern); + if (n == (size_t)-1) + { + xret = remove_upattern (param, pattern, op); + return ((xret == param) ? savestring (param) : xret); + } + n = xdupmbstowcs (&wparam, NULL, param); + if (n == (size_t)-1) + { + free (wpattern); + xret = remove_upattern (param, pattern, op); + return ((xret == param) ? savestring (param) : xret); + } + oret = ret = remove_wpattern (wparam, n, wpattern, op); + /* Don't bother to convert wparam back to multibyte string if nothing + matched; just return copy of original string */ + if (ret == wparam) + { + free (wparam); + free (wpattern); + return (savestring (param)); + } + + free (wparam); + free (wpattern); + + n = strlen (param); + xret = (char *)xmalloc (n + 1); + memset (&ps, '\0', sizeof (mbstate_t)); + n = wcsrtombs (xret, (const wchar_t **)&ret, n, &ps); + xret[n] = '\0'; /* just to make sure */ + free (oret); + return xret; + } + else +#endif + { + xret = remove_upattern (param, pattern, op); + return ((xret == param) ? savestring (param) : xret); + } +} + +/* Return 1 of the first character of STRING could match the first + character of pattern PAT. Used to avoid n2 calls to strmatch(). */ +static int +match_pattern_char (pat, string) + char *pat, *string; +{ + char c; + + if (*string == 0) + return (0); + + switch (c = *pat++) + { + default: + return (*string == c); + case '\\': + return (*string == *pat); + case '?': + return (*pat == LPAREN ? 1 : (*string != '\0')); + case '*': + return (1); + case '+': + case '!': + case '@': + return (*pat == LPAREN ? 1 : (*string == c)); + case '[': + return (*string != '\0'); + } +} + +/* Match PAT anywhere in STRING and return the match boundaries. + This returns 1 in case of a successful match, 0 otherwise. SP + and EP are pointers into the string where the match begins and + ends, respectively. MTYPE controls what kind of match is attempted. + MATCH_BEG and MATCH_END anchor the match at the beginning and end + of the string, respectively. The longest match is returned. */ +static int +match_upattern (string, pat, mtype, sp, ep) + char *string, *pat; + int mtype; + char **sp, **ep; +{ + int c, len; + register char *p, *p1, *npat; + char *end; + + /* If the pattern doesn't match anywhere in the string, go ahead and + short-circuit right away. A minor optimization, saves a bunch of + unnecessary calls to strmatch (up to N calls for a string of N + characters) if the match is unsuccessful. To preserve the semantics + of the substring matches below, we make sure that the pattern has + `*' as first and last character, making a new pattern if necessary. */ + /* XXX - check this later if I ever implement `**' with special meaning, + since this will potentially result in `**' at the beginning or end */ + len = STRLEN (pat); + if (pat[0] != '*' || (pat[0] == '*' && pat[1] == LPAREN && extended_glob) || pat[len - 1] != '*') + { + p = npat = (char *)xmalloc (len + 3); + p1 = pat; + if (*p1 != '*' || (*p1 == '*' && p1[1] == LPAREN && extended_glob)) + *p++ = '*'; + while (*p1) + *p++ = *p1++; + if (p1[-1] != '*' || p[-2] == '\\') + *p++ = '*'; + *p = '\0'; + } + else + npat = pat; + c = strmatch (npat, string, FNMATCH_EXTFLAG); + if (npat != pat) + free (npat); + if (c == FNM_NOMATCH) + return (0); + + len = STRLEN (string); + end = string + len; + + switch (mtype) + { + case MATCH_ANY: + for (p = string; p <= end; p++) + { + if (match_pattern_char (pat, p)) + { + for (p1 = end; p1 >= p; p1--) + { + c = *p1; *p1 = '\0'; + if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) + { + *p1 = c; + *sp = p; + *ep = p1; + return 1; + } + *p1 = c; + } + } + } + + return (0); + + case MATCH_BEG: + if (match_pattern_char (pat, string) == 0) + return (0); + + for (p = end; p >= string; p--) + { + c = *p; *p = '\0'; + if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0) + { + *p = c; + *sp = string; + *ep = p; + return 1; + } + *p = c; + } + + return (0); + + case MATCH_END: + for (p = string; p <= end; p++) + { + if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) + { + *sp = p; + *ep = end; + return 1; + } + + } + + return (0); + } + + return (0); +} + +#if defined (HANDLE_MULTIBYTE) +/* Return 1 of the first character of WSTRING could match the first + character of pattern WPAT. Wide character version. */ +static int +match_pattern_wchar (wpat, wstring) + wchar_t *wpat, *wstring; +{ + wchar_t wc; + + if (*wstring == 0) + return (0); + + switch (wc = *wpat++) + { + default: + return (*wstring == wc); + case L'\\': + return (*wstring == *wpat); + case L'?': + return (*wpat == LPAREN ? 1 : (*wstring != L'\0')); + case L'*': + return (1); + case L'+': + case L'!': + case L'@': + return (*wpat == LPAREN ? 1 : (*wstring == wc)); + case L'[': + return (*wstring != L'\0'); + } +} + +/* Match WPAT anywhere in WSTRING and return the match boundaries. + This returns 1 in case of a successful match, 0 otherwise. Wide + character version. */ +static int +match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) + wchar_t *wstring; + char **indices; + size_t wstrlen; + wchar_t *wpat; + int mtype; + char **sp, **ep; +{ + wchar_t wc, *wp, *nwpat, *wp1; + int len; +#if 0 + size_t n, n1; /* Apple's gcc seems to miscompile this badly */ +#else + int n, n1; +#endif + + /* If the pattern doesn't match anywhere in the string, go ahead and + short-circuit right away. A minor optimization, saves a bunch of + unnecessary calls to strmatch (up to N calls for a string of N + characters) if the match is unsuccessful. To preserve the semantics + of the substring matches below, we make sure that the pattern has + `*' as first and last character, making a new pattern if necessary. */ + /* XXX - check this later if I ever implement `**' with special meaning, + since this will potentially result in `**' at the beginning or end */ + len = wcslen (wpat); + if (wpat[0] != L'*' || (wpat[0] == L'*' && wpat[1] == WLPAREN && extended_glob) || wpat[len - 1] != L'*') + { + wp = nwpat = (wchar_t *)xmalloc ((len + 3) * sizeof (wchar_t)); + wp1 = wpat; + if (*wp1 != L'*' || (*wp1 == '*' && wp1[1] == WLPAREN && extended_glob)) + *wp++ = L'*'; + while (*wp1 != L'\0') + *wp++ = *wp1++; + if (wp1[-1] != L'*' || wp1[-2] == L'\\') + *wp++ = L'*'; + *wp = '\0'; + } + else + nwpat = wpat; + len = wcsmatch (nwpat, wstring, FNMATCH_EXTFLAG); + if (nwpat != wpat) + free (nwpat); + if (len == FNM_NOMATCH) + return (0); + + switch (mtype) + { + case MATCH_ANY: + for (n = 0; n <= wstrlen; n++) + { + if (match_pattern_wchar (wpat, wstring + n)) + { + for (n1 = wstrlen; n1 >= n; n1--) + { + wc = wstring[n1]; wstring[n1] = L'\0'; + if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) + { + wstring[n1] = wc; + *sp = indices[n]; + *ep = indices[n1]; + return 1; + } + wstring[n1] = wc; + } + } + } + + return (0); + + case MATCH_BEG: + if (match_pattern_wchar (wpat, wstring) == 0) + return (0); + + for (n = wstrlen; n >= 0; n--) + { + wc = wstring[n]; wstring[n] = L'\0'; + if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG) == 0) + { + wstring[n] = wc; + *sp = indices[0]; + *ep = indices[n]; + return 1; + } + wstring[n] = wc; + } + + return (0); + + case MATCH_END: + for (n = 0; n <= wstrlen; n++) + { + if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) + { + *sp = indices[n]; + *ep = indices[wstrlen]; + return 1; + } + } + + return (0); + } + + return (0); +} +#endif /* HANDLE_MULTIBYTE */ + +static int +match_pattern (string, pat, mtype, sp, ep) + char *string, *pat; + int mtype; + char **sp, **ep; +{ +#if defined (HANDLE_MULTIBYTE) + int ret; + size_t n; + wchar_t *wstring, *wpat; + char **indices; + static char *lstring = 0, *lpat = 0; /* cached last string, last pattern */ + static wchar_t *lwstring = 0, *lwpat = 0; /* cached wide last string, last pattern */ + static char **lindices = 0; + static size_t ln; +#endif + + if (string == 0 || *string == 0 || pat == 0 || *pat == 0) + return (0); + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + { + /* check cached values */ + if (pat != lpat) + { + free (lwpat); + lwpat = 0; + lpat = 0; + } + + if (string != lstring) + { + free (lwstring); + lwstring = 0; + free (lindices); + lindices = 0; + ln = 0; + lstring = 0; + } + + /* If necessary, compute new values */ + if (lwpat == 0) + { + n = xdupmbstowcs (&wpat, NULL, pat); + if (n == (size_t)-1) + { + free (lwpat); + lwpat = 0; + lpat = 0; + return (match_upattern (string, pat, mtype, sp, ep)); + } + } + else + wpat = lwpat; + + if (lwstring == 0) + { + n = xdupmbstowcs (&wstring, &indices, string); + if (n == (size_t)-1) + { + free (lwstring); + free (lindices); + lwstring = 0; + lindices = 0; + ln = 0; + lstring = 0; + return (match_upattern (string, pat, mtype, sp, ep)); + } + } + else + { + wstring = lwstring; + indices = lindices; + n = ln; + } + + ret = match_wpattern (wstring, indices, n, wpat, mtype, sp, ep); + + /* cache passed arguments */ + lstring = string; + lpat = pat; + + /* cache computed wide string values */ + lwpat = wpat; + lwstring = wstring; + lindices = indices; + ln = n; + + return (ret); + } + else +#endif + return (match_upattern (string, pat, mtype, sp, ep)); +} + +static int +getpatspec (c, value) + int c; + char *value; +{ + if (c == '#') + return ((*value == '#') ? RP_LONG_LEFT : RP_SHORT_LEFT); + else /* c == '%' */ + return ((*value == '%') ? RP_LONG_RIGHT : RP_SHORT_RIGHT); +} + +/* Posix.2 says that the WORD should be run through tilde expansion, + parameter expansion, command substitution and arithmetic expansion. + This leaves the result quoted, so quote_string_for_globbing () has + to be called to fix it up for strmatch (). If QUOTED is non-zero, + it means that the entire expression was enclosed in double quotes. + This means that quoting characters in the pattern do not make any + special pattern characters quoted. For example, the `*' in the + following retains its special meaning: "${foo#'*'}". */ +static char * +getpattern (value, quoted, expandpat) + char *value; + int quoted, expandpat; +{ + char *pat, *tword; + WORD_LIST *l; +#if 0 + int i; +#endif + /* There is a problem here: how to handle single or double quotes in the + pattern string when the whole expression is between double quotes? + POSIX.2 says that enclosing double quotes do not cause the pattern to + be quoted, but does that leave us a problem with @ and array[@] and their + expansions inside a pattern? */ +#if 0 + if (expandpat && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *tword) + { + i = 0; + pat = string_extract_double_quoted (tword, &i, 1); + free (tword); + tword = pat; + } +#endif + + /* expand_string_for_rhs () leaves WORD quoted and does not perform + word splitting. */ + l = *value ? expand_string_for_rhs (value, + (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? Q_PATQUOTE : quoted, + (int *)NULL, (int *)NULL) + : (WORD_LIST *)0; + pat = string_list (l); + dispose_words (l); + if (pat) + { + tword = quote_string_for_globbing (pat, QGLOB_CVTNULL); + free (pat); + pat = tword; + } + return (pat); +} + +#if 0 +/* Handle removing a pattern from a string as a result of ${name%[%]value} + or ${name#[#]value}. */ +static char * +variable_remove_pattern (value, pattern, patspec, quoted) + char *value, *pattern; + int patspec, quoted; +{ + char *tword; + + tword = remove_pattern (value, pattern, patspec); + + return (tword); +} +#endif + +static char * +list_remove_pattern (list, pattern, patspec, itype, quoted) + WORD_LIST *list; + char *pattern; + int patspec, itype, quoted; +{ + WORD_LIST *new, *l; + WORD_DESC *w; + char *tword; + + for (new = (WORD_LIST *)NULL, l = list; l; l = l->next) + { + tword = remove_pattern (l->word->word, pattern, patspec); + w = alloc_word_desc (); + w->word = tword ? tword : savestring (""); + new = make_word_list (w, new); + } + + l = REVERSE_LIST (new, WORD_LIST *); + tword = string_list_pos_params (itype, l, quoted); + dispose_words (l); + + return (tword); +} + +static char * +parameter_list_remove_pattern (itype, pattern, patspec, quoted) + int itype; + char *pattern; + int patspec, quoted; +{ + char *ret; + WORD_LIST *list; + + list = list_rest_of_args (); + if (list == 0) + return ((char *)NULL); + ret = list_remove_pattern (list, pattern, patspec, itype, quoted); + dispose_words (list); + return (ret); +} + +#if defined (ARRAY_VARS) +static char * +array_remove_pattern (var, pattern, patspec, varname, quoted) + SHELL_VAR *var; + char *pattern; + int patspec; + char *varname; /* so we can figure out how it's indexed */ + int quoted; +{ + ARRAY *a; + HASH_TABLE *h; + int itype; + char *ret; + WORD_LIST *list; + SHELL_VAR *v; + + /* compute itype from varname here */ + v = array_variable_part (varname, &ret, 0); + itype = ret[0]; + + a = (v && array_p (v)) ? array_cell (v) : 0; + h = (v && assoc_p (v)) ? assoc_cell (v) : 0; + + list = a ? array_to_word_list (a) : (h ? assoc_to_word_list (h) : 0); + if (list == 0) + return ((char *)NULL); + ret = list_remove_pattern (list, pattern, patspec, itype, quoted); + dispose_words (list); + + return ret; +} +#endif /* ARRAY_VARS */ + +static char * +parameter_brace_remove_pattern (varname, value, ind, patstr, rtype, quoted, flags) + char *varname, *value; + int ind; + char *patstr; + int rtype, quoted, flags; +{ + int vtype, patspec, starsub; + char *temp1, *val, *pattern; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + return ((char *)NULL); + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + patspec = getpatspec (rtype, patstr); + if (patspec == RP_LONG_LEFT || patspec == RP_LONG_RIGHT) + patstr++; + + /* Need to pass getpattern newly-allocated memory in case of expansion -- + the expansion code will free the passed string on an error. */ + temp1 = savestring (patstr); + pattern = getpattern (temp1, quoted, 1); + free (temp1); + + temp1 = (char *)NULL; /* shut up gcc */ + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + temp1 = remove_pattern (val, pattern, patspec); + if (vtype == VT_VARIABLE) + FREE (val); + if (temp1) + { + val = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + ? quote_string (temp1) + : quote_escapes (temp1); + free (temp1); + temp1 = val; + } + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + temp1 = array_remove_pattern (v, pattern, patspec, varname, quoted); + if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)) + { + val = quote_escapes (temp1); + free (temp1); + temp1 = val; + } + break; +#endif + case VT_POSPARMS: + temp1 = parameter_list_remove_pattern (varname[0], pattern, patspec, quoted); + if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)) + { + val = quote_escapes (temp1); + free (temp1); + temp1 = val; + } + break; + } + + FREE (pattern); + return temp1; +} + +/******************************************* + * * + * Functions to expand WORD_DESCs * + * * + *******************************************/ + +/* Expand WORD, performing word splitting on the result. This does + parameter expansion, command substitution, arithmetic expansion, + word splitting, and quote removal. */ + +WORD_LIST * +expand_word (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_LIST *result, *tresult; + + tresult = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL); + result = word_list_split (tresult); + dispose_words (tresult); + return (result ? dequote_list (result) : result); +} + +/* Expand WORD, but do not perform word splitting on the result. This + does parameter expansion, command substitution, arithmetic expansion, + and quote removal. */ +WORD_LIST * +expand_word_unsplit (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_LIST *result; + + expand_no_split_dollar_star = 1; +#if defined (HANDLE_MULTIBYTE) + if (ifs_firstc[0] == 0) +#else + if (ifs_firstc == 0) +#endif + word->flags |= W_NOSPLIT; + result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL); + expand_no_split_dollar_star = 0; + + return (result ? dequote_list (result) : result); +} + +/* Perform shell expansions on WORD, but do not perform word splitting or + quote removal on the result. Virtually identical to expand_word_unsplit; + could be combined if implementations don't diverge. */ +WORD_LIST * +expand_word_leave_quoted (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_LIST *result; + + expand_no_split_dollar_star = 1; +#if defined (HANDLE_MULTIBYTE) + if (ifs_firstc[0] == 0) +#else + if (ifs_firstc == 0) +#endif + word->flags |= W_NOSPLIT; + word->flags |= W_NOSPLIT2; + result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL); + expand_no_split_dollar_star = 0; + + return result; +} + +#if defined (PROCESS_SUBSTITUTION) + +/*****************************************************************/ +/* */ +/* Hacking Process Substitution */ +/* */ +/*****************************************************************/ + +#if !defined (HAVE_DEV_FD) +/* Named pipes must be removed explicitly with `unlink'. This keeps a list + of FIFOs the shell has open. unlink_fifo_list will walk the list and + unlink all of them. add_fifo_list adds the name of an open FIFO to the + list. NFIFO is a count of the number of FIFOs in the list. */ +#define FIFO_INCR 20 + +struct temp_fifo { + char *file; + pid_t proc; +}; + +static struct temp_fifo *fifo_list = (struct temp_fifo *)NULL; +static int nfifo; +static int fifo_list_size; + +char * +copy_fifo_list (sizep) + int *sizep; +{ + if (sizep) + *sizep = 0; + return (char *)NULL; +} + +static void +add_fifo_list (pathname) + char *pathname; +{ + if (nfifo >= fifo_list_size - 1) + { + fifo_list_size += FIFO_INCR; + fifo_list = (struct temp_fifo *)xrealloc (fifo_list, + fifo_list_size * sizeof (struct temp_fifo)); + } + + fifo_list[nfifo].file = savestring (pathname); + nfifo++; +} + +void +unlink_fifo (i) + int i; +{ + if ((fifo_list[i].proc == -1) || (kill(fifo_list[i].proc, 0) == -1)) + { + unlink (fifo_list[i].file); + free (fifo_list[i].file); + fifo_list[i].file = (char *)NULL; + fifo_list[i].proc = -1; + } +} + +void +unlink_fifo_list () +{ + int saved, i, j; + + if (nfifo == 0) + return; + + for (i = saved = 0; i < nfifo; i++) + { + if ((fifo_list[i].proc == -1) || (kill(fifo_list[i].proc, 0) == -1)) + { + unlink (fifo_list[i].file); + free (fifo_list[i].file); + fifo_list[i].file = (char *)NULL; + fifo_list[i].proc = -1; + } + else + saved++; + } + + /* If we didn't remove some of the FIFOs, compact the list. */ + if (saved) + { + for (i = j = 0; i < nfifo; i++) + if (fifo_list[i].file) + { + fifo_list[j].file = fifo_list[i].file; + fifo_list[j].proc = fifo_list[i].proc; + j++; + } + nfifo = j; + } + else + nfifo = 0; +} + +/* Take LIST, which is a bitmap denoting active FIFOs in fifo_list + from some point in the past, and close all open FIFOs in fifo_list + that are not marked as active in LIST. If LIST is NULL, close + everything in fifo_list. LSIZE is the number of elements in LIST, in + case it's larger than fifo_list_size (size of fifo_list). */ +void +close_new_fifos (list, lsize) + char *list; + int lsize; +{ + int i; + + if (list == 0) + { + unlink_fifo_list (); + return; + } + + for (i = 0; i < lsize; i++) + if (list[i] == 0 && i < fifo_list_size && fifo_list[i].proc != -1) + unlink_fifo (i); + + for (i = lsize; i < fifo_list_size; i++) + unlink_fifo (i); +} + +int +fifos_pending () +{ + return nfifo; +} + +int +num_fifos () +{ + return nfifo; +} + +static char * +make_named_pipe () +{ + char *tname; + + tname = sh_mktmpname ("sh-np", MT_USERANDOM|MT_USETMPDIR); + if (mkfifo (tname, 0600) < 0) + { + free (tname); + return ((char *)NULL); + } + + add_fifo_list (tname); + return (tname); +} + +#else /* HAVE_DEV_FD */ + +/* DEV_FD_LIST is a bitmap of file descriptors attached to pipes the shell + has open to children. NFDS is a count of the number of bits currently + set in DEV_FD_LIST. TOTFDS is a count of the highest possible number + of open files. */ +static char *dev_fd_list = (char *)NULL; +static int nfds; +static int totfds; /* The highest possible number of open files. */ + +char * +copy_fifo_list (sizep) + int *sizep; +{ + char *ret; + + if (nfds == 0 || totfds == 0) + { + if (sizep) + *sizep = 0; + return (char *)NULL; + } + + if (sizep) + *sizep = totfds; + ret = (char *)xmalloc (totfds); + return (memcpy (ret, dev_fd_list, totfds)); +} + +static void +add_fifo_list (fd) + int fd; +{ + if (dev_fd_list == 0 || fd >= totfds) + { + int ofds; + + ofds = totfds; + totfds = getdtablesize (); + if (totfds < 0 || totfds > 256) + totfds = 256; + if (fd >= totfds) + totfds = fd + 2; + + dev_fd_list = (char *)xrealloc (dev_fd_list, totfds); + memset (dev_fd_list + ofds, '\0', totfds - ofds); + } + + dev_fd_list[fd] = 1; + nfds++; +} + +int +fifos_pending () +{ + return 0; /* used for cleanup; not needed with /dev/fd */ +} + +int +num_fifos () +{ + return nfds; +} + +void +unlink_fifo (fd) + int fd; +{ + if (dev_fd_list[fd]) + { + close (fd); + dev_fd_list[fd] = 0; + nfds--; + } +} + +void +unlink_fifo_list () +{ + register int i; + + if (nfds == 0) + return; + + for (i = 0; nfds && i < totfds; i++) + unlink_fifo (i); + + nfds = 0; +} + +/* Take LIST, which is a snapshot copy of dev_fd_list from some point in + the past, and close all open fds in dev_fd_list that are not marked + as open in LIST. If LIST is NULL, close everything in dev_fd_list. + LSIZE is the number of elements in LIST, in case it's larger than + totfds (size of dev_fd_list). */ +void +close_new_fifos (list, lsize) + char *list; + int lsize; +{ + int i; + + if (list == 0) + { + unlink_fifo_list (); + return; + } + + for (i = 0; i < lsize; i++) + if (list[i] == 0 && i < totfds && dev_fd_list[i]) + unlink_fifo (i); + + for (i = lsize; i < totfds; i++) + unlink_fifo (i); +} + +#if defined (NOTDEF) +print_dev_fd_list () +{ + register int i; + + fprintf (stderr, "pid %ld: dev_fd_list:", (long)getpid ()); + fflush (stderr); + + for (i = 0; i < totfds; i++) + { + if (dev_fd_list[i]) + fprintf (stderr, " %d", i); + } + fprintf (stderr, "\n"); +} +#endif /* NOTDEF */ + +static char * +make_dev_fd_filename (fd) + int fd; +{ + char *ret, intbuf[INT_STRLEN_BOUND (int) + 1], *p; + + ret = (char *)xmalloc (sizeof (DEV_FD_PREFIX) + 8); + + strcpy (ret, DEV_FD_PREFIX); + p = inttostr (fd, intbuf, sizeof (intbuf)); + strcpy (ret + sizeof (DEV_FD_PREFIX) - 1, p); + + add_fifo_list (fd); + return (ret); +} + +#endif /* HAVE_DEV_FD */ + +/* Return a filename that will open a connection to the process defined by + executing STRING. HAVE_DEV_FD, if defined, means open a pipe and return + a filename in /dev/fd corresponding to a descriptor that is one of the + ends of the pipe. If not defined, we use named pipes on systems that have + them. Systems without /dev/fd and named pipes are out of luck. + + OPEN_FOR_READ_IN_CHILD, if 1, means open the named pipe for reading or + use the read end of the pipe and dup that file descriptor to fd 0 in + the child. If OPEN_FOR_READ_IN_CHILD is 0, we open the named pipe for + writing or use the write end of the pipe in the child, and dup that + file descriptor to fd 1 in the child. The parent does the opposite. */ + +static char * +process_substitute (string, open_for_read_in_child) + char *string; + int open_for_read_in_child; +{ + char *pathname; + int fd, result; + pid_t old_pid, pid; +#if defined (HAVE_DEV_FD) + int parent_pipe_fd, child_pipe_fd; + int fildes[2]; +#endif /* HAVE_DEV_FD */ +#if defined (JOB_CONTROL) + pid_t old_pipeline_pgrp; +#endif + + if (!string || !*string || wordexp_only) + return ((char *)NULL); + +#if !defined (HAVE_DEV_FD) + pathname = make_named_pipe (); +#else /* HAVE_DEV_FD */ + if (pipe (fildes) < 0) + { + sys_error (_("cannot make pipe for process substitution")); + return ((char *)NULL); + } + /* If OPEN_FOR_READ_IN_CHILD == 1, we want to use the write end of + the pipe in the parent, otherwise the read end. */ + parent_pipe_fd = fildes[open_for_read_in_child]; + child_pipe_fd = fildes[1 - open_for_read_in_child]; + /* Move the parent end of the pipe to some high file descriptor, to + avoid clashes with FDs used by the script. */ + parent_pipe_fd = move_to_high_fd (parent_pipe_fd, 1, 64); + + pathname = make_dev_fd_filename (parent_pipe_fd); +#endif /* HAVE_DEV_FD */ + + if (pathname == 0) + { + sys_error (_("cannot make pipe for process substitution")); + return ((char *)NULL); + } + + old_pid = last_made_pid; + +#if defined (JOB_CONTROL) + old_pipeline_pgrp = pipeline_pgrp; + pipeline_pgrp = shell_pgrp; + save_pipeline (1); +#endif /* JOB_CONTROL */ + + pid = make_child ((char *)NULL, 1); + if (pid == 0) + { + reset_terminating_signals (); /* XXX */ + free_pushed_string_input (); + /* Cancel traps, in trap.c. */ + restore_original_signals (); /* XXX - what about special builtins? bash-4.2 */ + setup_async_signals (); + subshell_environment |= SUBSHELL_COMSUB|SUBSHELL_PROCSUB; + } + +#if defined (JOB_CONTROL) + set_sigchld_handler (); + stop_making_children (); + /* XXX - should we only do this in the parent? (as in command subst) */ + pipeline_pgrp = old_pipeline_pgrp; +#endif /* JOB_CONTROL */ + + if (pid < 0) + { + sys_error (_("cannot make child for process substitution")); + free (pathname); +#if defined (HAVE_DEV_FD) + close (parent_pipe_fd); + close (child_pipe_fd); +#endif /* HAVE_DEV_FD */ + return ((char *)NULL); + } + + if (pid > 0) + { +#if defined (JOB_CONTROL) + restore_pipeline (1); +#endif + +#if !defined (HAVE_DEV_FD) + fifo_list[nfifo-1].proc = pid; +#endif + + last_made_pid = old_pid; + +#if defined (JOB_CONTROL) && defined (PGRP_PIPE) + close_pgrp_pipe (); +#endif /* JOB_CONTROL && PGRP_PIPE */ + +#if defined (HAVE_DEV_FD) + close (child_pipe_fd); +#endif /* HAVE_DEV_FD */ + + return (pathname); + } + + set_sigint_handler (); + +#if defined (JOB_CONTROL) + set_job_control (0); +#endif /* JOB_CONTROL */ + +#if !defined (HAVE_DEV_FD) + /* Open the named pipe in the child. */ + fd = open (pathname, open_for_read_in_child ? O_RDONLY|O_NONBLOCK : O_WRONLY); + if (fd < 0) + { + /* Two separate strings for ease of translation. */ + if (open_for_read_in_child) + sys_error (_("cannot open named pipe %s for reading"), pathname); + else + sys_error (_("cannot open named pipe %s for writing"), pathname); + + exit (127); + } + if (open_for_read_in_child) + { + if (sh_unset_nodelay_mode (fd) < 0) + { + sys_error (_("cannot reset nodelay mode for fd %d"), fd); + exit (127); + } + } +#else /* HAVE_DEV_FD */ + fd = child_pipe_fd; +#endif /* HAVE_DEV_FD */ + + if (dup2 (fd, open_for_read_in_child ? 0 : 1) < 0) + { + sys_error (_("cannot duplicate named pipe %s as fd %d"), pathname, + open_for_read_in_child ? 0 : 1); + exit (127); + } + + if (fd != (open_for_read_in_child ? 0 : 1)) + close (fd); + + /* Need to close any files that this process has open to pipes inherited + from its parent. */ + if (current_fds_to_close) + { + close_fd_bitmap (current_fds_to_close); + current_fds_to_close = (struct fd_bitmap *)NULL; + } + +#if defined (HAVE_DEV_FD) + /* Make sure we close the parent's end of the pipe and clear the slot + in the fd list so it is not closed later, if reallocated by, for + instance, pipe(2). */ + close (parent_pipe_fd); + dev_fd_list[parent_pipe_fd] = 0; +#endif /* HAVE_DEV_FD */ + + result = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST)); + +#if !defined (HAVE_DEV_FD) + /* Make sure we close the named pipe in the child before we exit. */ + close (open_for_read_in_child ? 0 : 1); +#endif /* !HAVE_DEV_FD */ + + exit (result); + /*NOTREACHED*/ +} +#endif /* PROCESS_SUBSTITUTION */ + +/***********************************/ +/* */ +/* Command Substitution */ +/* */ +/***********************************/ + +static char * +read_comsub (fd, quoted, rflag) + int fd, quoted; + int *rflag; +{ + char *istring, buf[128], *bufp, *s; + int istring_index, istring_size, c, tflag, skip_ctlesc, skip_ctlnul; + ssize_t bufn; + + istring = (char *)NULL; + istring_index = istring_size = bufn = tflag = 0; + + for (skip_ctlesc = skip_ctlnul = 0, s = ifs_value; s && *s; s++) + skip_ctlesc |= *s == CTLESC, skip_ctlnul |= *s == CTLNUL; + +#ifdef __CYGWIN__ + setmode (fd, O_TEXT); /* we don't want CR/LF, we want Unix-style */ +#endif + + /* Read the output of the command through the pipe. This may need to be + changed to understand multibyte characters in the future. */ + while (1) + { + if (fd < 0) + break; + if (--bufn <= 0) + { + bufn = zread (fd, buf, sizeof (buf)); + if (bufn <= 0) + break; + bufp = buf; + } + c = *bufp++; + + if (c == 0) + { +#if 0 + internal_warning ("read_comsub: ignored null byte in input"); +#endif + continue; + } + + /* Add the character to ISTRING, possibly after resizing it. */ + RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, DEFAULT_ARRAY_SIZE); + + /* This is essentially quote_string inline */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) /* || c == CTLESC || c == CTLNUL */) + istring[istring_index++] = CTLESC; + /* Escape CTLESC and CTLNUL in the output to protect those characters + from the rest of the word expansions (word splitting and globbing.) + This is essentially quote_escapes inline. */ + else if (skip_ctlesc == 0 && c == CTLESC) + { + tflag |= W_HASCTLESC; + istring[istring_index++] = CTLESC; + } + else if ((skip_ctlnul == 0 && c == CTLNUL) || (c == ' ' && (ifs_value && *ifs_value == 0))) + istring[istring_index++] = CTLESC; + + istring[istring_index++] = c; + +#if 0 +#if defined (__CYGWIN__) + if (c == '\n' && istring_index > 1 && istring[istring_index - 2] == '\r') + { + istring_index--; + istring[istring_index - 1] = '\n'; + } +#endif +#endif + } + + if (istring) + istring[istring_index] = '\0'; + + /* If we read no output, just return now and save ourselves some + trouble. */ + if (istring_index == 0) + { + FREE (istring); + if (rflag) + *rflag = tflag; + return (char *)NULL; + } + + /* Strip trailing newlines from the output of the command. */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + { + while (istring_index > 0) + { + if (istring[istring_index - 1] == '\n') + { + --istring_index; + + /* If the newline was quoted, remove the quoting char. */ + if (istring[istring_index - 1] == CTLESC) + --istring_index; + } + else + break; + } + istring[istring_index] = '\0'; + } + else + strip_trailing (istring, istring_index - 1, 1); + + if (rflag) + *rflag = tflag; + return istring; +} + +/* Perform command substitution on STRING. This returns a WORD_DESC * with the + contained string possibly quoted. */ +WORD_DESC * +command_substitute (string, quoted) + char *string; + int quoted; +{ + pid_t pid, old_pid, old_pipeline_pgrp, old_async_pid; + char *istring; + int result, fildes[2], function_value, pflags, rc, tflag; + WORD_DESC *ret; + + istring = (char *)NULL; + + /* Don't fork () if there is no need to. In the case of no command to + run, just return NULL. */ + if (!string || !*string || (string[0] == '\n' && !string[1])) + return ((WORD_DESC *)NULL); + + if (wordexp_only && read_but_dont_execute) + { + last_command_exit_value = EX_WEXPCOMSUB; + jump_to_top_level (EXITPROG); + } + + /* We're making the assumption here that the command substitution will + eventually run a command from the file system. Since we'll run + maybe_make_export_env in this subshell before executing that command, + the parent shell and any other shells it starts will have to remake + the environment. If we make it before we fork, other shells won't + have to. Don't bother if we have any temporary variable assignments, + though, because the export environment will be remade after this + command completes anyway, but do it if all the words to be expanded + are variable assignments. */ + if (subst_assign_varlist == 0 || garglist == 0) + maybe_make_export_env (); /* XXX */ + + /* Flags to pass to parse_and_execute() */ + pflags = (interactive && sourcelevel == 0) ? SEVAL_RESETLINE : 0; + + /* Pipe the output of executing STRING into the current shell. */ + if (pipe (fildes) < 0) + { + sys_error (_("cannot make pipe for command substitution")); + goto error_exit; + } + + old_pid = last_made_pid; +#if defined (JOB_CONTROL) + old_pipeline_pgrp = pipeline_pgrp; + /* Don't reset the pipeline pgrp if we're already a subshell in a pipeline. */ + if ((subshell_environment & SUBSHELL_PIPE) == 0) + pipeline_pgrp = shell_pgrp; + cleanup_the_pipeline (); +#endif /* JOB_CONTROL */ + + old_async_pid = last_asynchronous_pid; + pid = make_child ((char *)NULL, subshell_environment&SUBSHELL_ASYNC); + last_asynchronous_pid = old_async_pid; + + if (pid == 0) + { + /* Reset the signal handlers in the child, but don't free the + trap strings. Set a flag noting that we have to free the + trap strings if we run trap to change a signal disposition. */ + reset_signal_handlers (); + subshell_environment |= SUBSHELL_RESETTRAP; + } + +#if defined (JOB_CONTROL) + /* XXX DO THIS ONLY IN PARENT ? XXX */ + set_sigchld_handler (); + stop_making_children (); + if (pid != 0) + pipeline_pgrp = old_pipeline_pgrp; +#else + stop_making_children (); +#endif /* JOB_CONTROL */ + + if (pid < 0) + { + sys_error (_("cannot make child for command substitution")); + error_exit: + + FREE (istring); + close (fildes[0]); + close (fildes[1]); + return ((WORD_DESC *)NULL); + } + + if (pid == 0) + { + set_sigint_handler (); /* XXX */ + + free_pushed_string_input (); + + if (dup2 (fildes[1], 1) < 0) + { + sys_error (_("command_substitute: cannot duplicate pipe as fd 1")); + exit (EXECUTION_FAILURE); + } + + /* If standard output is closed in the parent shell + (such as after `exec >&-'), file descriptor 1 will be + the lowest available file descriptor, and end up in + fildes[0]. This can happen for stdin and stderr as well, + but stdout is more important -- it will cause no output + to be generated from this command. */ + if ((fildes[1] != fileno (stdin)) && + (fildes[1] != fileno (stdout)) && + (fildes[1] != fileno (stderr))) + close (fildes[1]); + + if ((fildes[0] != fileno (stdin)) && + (fildes[0] != fileno (stdout)) && + (fildes[0] != fileno (stderr))) + close (fildes[0]); + + /* The currently executing shell is not interactive. */ + interactive = 0; + + /* This is a subshell environment. */ + subshell_environment |= SUBSHELL_COMSUB; + + /* When not in POSIX mode, command substitution does not inherit + the -e flag. */ + if (posixly_correct == 0) + exit_immediately_on_error = 0; + + remove_quoted_escapes (string); + + startup_state = 2; /* see if we can avoid a fork */ + /* Give command substitution a place to jump back to on failure, + so we don't go back up to main (). */ + result = setjmp (top_level); + + /* If we're running a command substitution inside a shell function, + trap `return' so we don't return from the function in the subshell + and go off to never-never land. */ + if (result == 0 && return_catch_flag) + function_value = setjmp (return_catch); + else + function_value = 0; + + if (result == ERREXIT) + rc = last_command_exit_value; + else if (result == EXITPROG) + rc = last_command_exit_value; + else if (result) + rc = EXECUTION_FAILURE; + else if (function_value) + rc = return_catch_value; + else + { + subshell_level++; + rc = parse_and_execute (string, "command substitution", pflags|SEVAL_NOHIST); + subshell_level--; + } + + last_command_exit_value = rc; + rc = run_exit_trap (); +#if defined (PROCESS_SUBSTITUTION) + unlink_fifo_list (); +#endif + exit (rc); + } + else + { +#if defined (JOB_CONTROL) && defined (PGRP_PIPE) + close_pgrp_pipe (); +#endif /* JOB_CONTROL && PGRP_PIPE */ + + close (fildes[1]); + + tflag = 0; + istring = read_comsub (fildes[0], quoted, &tflag); + + close (fildes[0]); + + current_command_subst_pid = pid; + last_command_exit_value = wait_for (pid); + last_command_subst_pid = pid; + last_made_pid = old_pid; + +#if defined (JOB_CONTROL) + /* If last_command_exit_value > 128, then the substituted command + was terminated by a signal. If that signal was SIGINT, then send + SIGINT to ourselves. This will break out of loops, for instance. */ + if (last_command_exit_value == (128 + SIGINT) && last_command_exit_signal == SIGINT) + kill (getpid (), SIGINT); + + /* wait_for gives the terminal back to shell_pgrp. If some other + process group should have it, give it away to that group here. + pipeline_pgrp is non-zero only while we are constructing a + pipline, so what we are concerned about is whether or not that + pipeline was started in the background. A pipeline started in + the background should never get the tty back here. */ + if (interactive && pipeline_pgrp != (pid_t)0 && (subshell_environment & SUBSHELL_ASYNC) == 0) + give_terminal_to (pipeline_pgrp, 0); +#endif /* JOB_CONTROL */ + + ret = alloc_word_desc (); + ret->word = istring; + ret->flags = tflag; + + return ret; + } +} + +/******************************************************** + * * + * Utility functions for parameter expansion * + * * + ********************************************************/ + +#if defined (ARRAY_VARS) + +static arrayind_t +array_length_reference (s) + char *s; +{ + int len; + arrayind_t ind; + char *akey; + char *t, c; + ARRAY *array; + HASH_TABLE *h; + SHELL_VAR *var; + + var = array_variable_part (s, &t, &len); + + /* If unbound variables should generate an error, report one and return + failure. */ + if ((var == 0 || (assoc_p (var) == 0 && array_p (var) == 0)) && unbound_vars_is_error) + { + c = *--t; + *t = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (s); + *t = c; + return (-1); + } + else if (var == 0) + return 0; + + /* We support a couple of expansions for variables that are not arrays. + We'll return the length of the value for v[0], and 1 for v[@] or + v[*]. Return 0 for everything else. */ + + array = array_p (var) ? array_cell (var) : (ARRAY *)NULL; + h = assoc_p (var) ? assoc_cell (var) : (HASH_TABLE *)NULL; + + if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']') + { + if (assoc_p (var)) + return (h ? assoc_num_elements (h) : 0); + else if (array_p (var)) + return (array ? array_num_elements (array) : 0); + else + return (var_isset (var) ? 1 : 0); + } + + if (assoc_p (var)) + { + t[len - 1] = '\0'; + akey = expand_assignment_string_to_string (t, 0); /* [ */ + t[len - 1] = ']'; + if (akey == 0 || *akey == 0) + { + err_badarraysub (t); + return (-1); + } + t = assoc_reference (assoc_cell (var), akey); + } + else + { + ind = array_expand_index (t, len); + if (ind < 0) + { + err_badarraysub (t); + return (-1); + } + if (array_p (var)) + t = array_reference (array, ind); + else + t = (ind == 0) ? value_cell (var) : (char *)NULL; + } + + len = MB_STRLEN (t); + return (len); +} +#endif /* ARRAY_VARS */ + +static int +valid_brace_expansion_word (name, var_is_special) + char *name; + int var_is_special; +{ + if (DIGIT (*name) && all_digits (name)) + return 1; + else if (var_is_special) + return 1; +#if defined (ARRAY_VARS) + else if (valid_array_reference (name)) + return 1; +#endif /* ARRAY_VARS */ + else if (legal_identifier (name)) + return 1; + else + return 0; +} + +static int +chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at) + char *name; + int quoted; + int *quoted_dollar_atp, *contains_dollar_at; +{ + char *temp1; + + if (name == 0) + { + if (quoted_dollar_atp) + *quoted_dollar_atp = 0; + if (contains_dollar_at) + *contains_dollar_at = 0; + return 0; + } + + /* check for $@ and $* */ + if (name[0] == '@' && name[1] == 0) + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } + else if (name[0] == '*' && name[1] == '\0' && quoted == 0) + { + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } + + /* Now check for ${array[@]} and ${array[*]} */ +#if defined (ARRAY_VARS) + else if (valid_array_reference (name)) + { + temp1 = mbschr (name, '['); + if (temp1 && temp1[1] == '@' && temp1[2] == ']') + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } /* [ */ + /* ${array[*]}, when unquoted, should be treated like ${array[@]}, + which should result in separate words even when IFS is unset. */ + if (temp1 && temp1[1] == '*' && temp1[2] == ']' && quoted == 0) + { + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } + } +#endif + return 0; +} + +/* Parameter expand NAME, and return a new string which is the expansion, + or NULL if there was no expansion. + VAR_IS_SPECIAL is non-zero if NAME is one of the special variables in + the shell, e.g., "@", "$", "*", etc. QUOTED, if non-zero, means that + NAME was found inside of a double-quoted expression. */ +static WORD_DESC * +parameter_brace_expand_word (name, var_is_special, quoted, pflags, indp) + char *name; + int var_is_special, quoted, pflags; + arrayind_t *indp; +{ + WORD_DESC *ret; + char *temp, *tt; + intmax_t arg_index; + SHELL_VAR *var; + int atype, rflags; + arrayind_t ind; + + ret = 0; + temp = 0; + rflags = 0; + + if (indp) + *indp = INTMAX_MIN; + + /* Handle multiple digit arguments, as in ${11}. */ + if (legal_number (name, &arg_index)) + { + tt = get_dollar_var_value (arg_index); + if (tt) + temp = (*tt && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (tt) + : quote_escapes (tt); + else + temp = (char *)NULL; + FREE (tt); + } + else if (var_is_special) /* ${@} */ + { + int sindex; + tt = (char *)xmalloc (2 + strlen (name)); + tt[sindex = 0] = '$'; + strcpy (tt + 1, name); + + ret = param_expand (tt, &sindex, quoted, (int *)NULL, (int *)NULL, + (int *)NULL, (int *)NULL, pflags); + free (tt); + } +#if defined (ARRAY_VARS) + else if (valid_array_reference (name)) + { + temp = array_value (name, quoted, 0, &atype, &ind); + if (atype == 0 && temp) + { + temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (temp) + : quote_escapes (temp); + rflags |= W_ARRAYIND; + if (indp) + *indp = ind; + } + else if (atype == 1 && temp && QUOTED_NULL (temp) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + rflags |= W_HASQUOTEDNULL; + } +#endif + else if (var = find_variable (name)) + { + if (var_isset (var) && invisible_p (var) == 0) + { +#if defined (ARRAY_VARS) + if (assoc_p (var)) + temp = assoc_reference (assoc_cell (var), "0"); + else if (array_p (var)) + temp = array_reference (array_cell (var), 0); + else + temp = value_cell (var); +#else + temp = value_cell (var); +#endif + + if (temp) + temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (temp) + : quote_escapes (temp); + } + else + temp = (char *)NULL; + } + else + temp = (char *)NULL; + + if (ret == 0) + { + ret = alloc_word_desc (); + ret->word = temp; + ret->flags |= rflags; + } + return ret; +} + +/* Expand an indirect reference to a variable: ${!NAME} expands to the + value of the variable whose name is the value of NAME. */ +static WORD_DESC * +parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at) + char *name; + int var_is_special, quoted; + int *quoted_dollar_atp, *contains_dollar_at; +{ + char *temp, *t; + WORD_DESC *w; + + w = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND, 0); + t = w->word; + /* Have to dequote here if necessary */ + if (t) + { + temp = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + ? dequote_string (t) + : dequote_escapes (t); + free (t); + t = temp; + } + dispose_word_desc (w); + + chk_atstar (t, quoted, quoted_dollar_atp, contains_dollar_at); + if (t == 0) + return (WORD_DESC *)NULL; + + w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0, 0); + free (t); + + return w; +} + +/* Expand the right side of a parameter expansion of the form ${NAMEcVALUE}, + depending on the value of C, the separating character. C can be one of + "-", "+", or "=". QUOTED is true if the entire brace expression occurs + between double quotes. */ +static WORD_DESC * +parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat) + char *name, *value; + int c, quoted, *qdollaratp, *hasdollarat; +{ + WORD_DESC *w; + WORD_LIST *l; + char *t, *t1, *temp; + int hasdol; + + /* If the entire expression is between double quotes, we want to treat + the value as a double-quoted string, with the exception that we strip + embedded unescaped double quotes (for sh backwards compatibility). */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *value) + { + hasdol = 0; + temp = string_extract_double_quoted (value, &hasdol, 1); + } + else + temp = value; + + w = alloc_word_desc (); + hasdol = 0; + /* XXX was 0 not quoted */ + l = *temp ? expand_string_for_rhs (temp, quoted, &hasdol, (int *)NULL) + : (WORD_LIST *)0; + if (hasdollarat) + *hasdollarat = hasdol || (l && l->next); + if (temp != value) + free (temp); + if (l) + { + /* The expansion of TEMP returned something. We need to treat things + slightly differently if HASDOL is non-zero. If we have "$@", the + individual words have already been quoted. We need to turn them + into a string with the words separated by the first character of + $IFS without any additional quoting, so string_list_dollar_at won't + do the right thing. We use string_list_dollar_star instead. */ + temp = (hasdol || l->next) ? string_list_dollar_star (l) : string_list (l); + + /* If l->next is not null, we know that TEMP contained "$@", since that + is the only expansion that creates more than one word. */ + if (qdollaratp && ((hasdol && quoted) || l->next)) + *qdollaratp = 1; + dispose_words (l); + } + else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && hasdol) + { + /* The brace expansion occurred between double quotes and there was + a $@ in TEMP. It does not matter if the $@ is quoted, as long as + it does not expand to anything. In this case, we want to return + a quoted empty string. */ + temp = make_quoted_char ('\0'); + w->flags |= W_HASQUOTEDNULL; + } + else + temp = (char *)NULL; + + if (c == '-' || c == '+') + { + w->word = temp; + return w; + } + + /* c == '=' */ + t = temp ? savestring (temp) : savestring (""); + t1 = dequote_string (t); + free (t); +#if defined (ARRAY_VARS) + if (valid_array_reference (name)) + assign_array_element (name, t1, 0); + else +#endif /* ARRAY_VARS */ + bind_variable (name, t1, 0); + + /* From Posix group discussion Feb-March 2010. Issue 7 0000221 */ + free (temp); + + w->word = t1; + return w; +} + +/* Deal with the right hand side of a ${name:?value} expansion in the case + that NAME is null or not set. If VALUE is non-null it is expanded and + used as the error message to print, otherwise a standard message is + printed. */ +static void +parameter_brace_expand_error (name, value) + char *name, *value; +{ + WORD_LIST *l; + char *temp; + + if (value && *value) + { + l = expand_string (value, 0); + temp = string_list (l); + report_error ("%s: %s", name, temp ? temp : ""); /* XXX was value not "" */ + FREE (temp); + dispose_words (l); + } + else + report_error (_("%s: parameter null or not set"), name); + + /* Free the data we have allocated during this expansion, since we + are about to longjmp out. */ + free (name); + FREE (value); +} + +/* Return 1 if NAME is something for which parameter_brace_expand_length is + OK to do. */ +static int +valid_length_expression (name) + char *name; +{ + return (name[1] == '\0' || /* ${#} */ + ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0') || /* special param */ + (DIGIT (name[1]) && all_digits (name + 1)) || /* ${#11} */ +#if defined (ARRAY_VARS) + valid_array_reference (name + 1) || /* ${#a[7]} */ +#endif + legal_identifier (name + 1)); /* ${#PS1} */ +} + +#if defined (HANDLE_MULTIBYTE) +size_t +mbstrlen (s) + const char *s; +{ + size_t clen, nc; + mbstate_t mbs, mbsbak; + + nc = 0; + memset (&mbs, 0, sizeof (mbs)); + mbsbak = mbs; + while ((clen = mbrlen(s, MB_CUR_MAX, &mbs)) != 0) + { + if (MB_INVALIDCH(clen)) + { + clen = 1; /* assume single byte */ + mbs = mbsbak; + } + + s += clen; + nc++; + mbsbak = mbs; + } + return nc; +} +#endif + + +/* Handle the parameter brace expansion that requires us to return the + length of a parameter. */ +static intmax_t +parameter_brace_expand_length (name) + char *name; +{ + char *t, *newname; + intmax_t number, arg_index; + WORD_LIST *list; +#if defined (ARRAY_VARS) + SHELL_VAR *var; +#endif + + if (name[1] == '\0') /* ${#} */ + number = number_of_args (); + else if ((name[1] == '@' || name[1] == '*') && name[2] == '\0') /* ${#@}, ${#*} */ + number = number_of_args (); + else if ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0') + { + /* Take the lengths of some of the shell's special parameters. */ + switch (name[1]) + { + case '-': + t = which_set_flags (); + break; + case '?': + t = itos (last_command_exit_value); + break; + case '$': + t = itos (dollar_dollar_pid); + break; + case '!': + if (last_asynchronous_pid == NO_PID) + t = (char *)NULL; + else + t = itos (last_asynchronous_pid); + break; + case '#': + t = itos (number_of_args ()); + break; + } + number = STRLEN (t); + FREE (t); + } +#if defined (ARRAY_VARS) + else if (valid_array_reference (name + 1)) + number = array_length_reference (name + 1); +#endif /* ARRAY_VARS */ + else + { + number = 0; + + if (legal_number (name + 1, &arg_index)) /* ${#1} */ + { + t = get_dollar_var_value (arg_index); + number = MB_STRLEN (t); + FREE (t); + } +#if defined (ARRAY_VARS) + else if ((var = find_variable (name + 1)) && (invisible_p (var) == 0) && (array_p (var) || assoc_p (var))) + { + if (assoc_p (var)) + t = assoc_reference (assoc_cell (var), "0"); + else + t = array_reference (array_cell (var), 0); + number = MB_STRLEN (t); + } +#endif + else /* ${#PS1} */ + { + newname = savestring (name); + newname[0] = '$'; + list = expand_string (newname, Q_DOUBLE_QUOTES); + t = list ? string_list (list) : (char *)NULL; + free (newname); + if (list) + dispose_words (list); + + number = MB_STRLEN (t); + FREE (t); + } + } + + return (number); +} + +/* Skip characters in SUBSTR until DELIM. SUBSTR is an arithmetic expression, + so we do some ad-hoc parsing of an arithmetic expression to find + the first DELIM, instead of using strchr(3). Two rules: + 1. If the substring contains a `(', read until closing `)'. + 2. If the substring contains a `?', read past one `:' for each `?'. +*/ + +static char * +skiparith (substr, delim) + char *substr; + int delim; +{ + size_t sublen; + int skipcol, pcount, i; + DECLARE_MBSTATE; + + sublen = strlen (substr); + i = skipcol = pcount = 0; + while (substr[i]) + { + /* Balance parens */ + if (substr[i] == LPAREN) + { + pcount++; + i++; + continue; + } + if (substr[i] == RPAREN && pcount) + { + pcount--; + i++; + continue; + } + if (pcount) + { + ADVANCE_CHAR (substr, sublen, i); + continue; + } + + /* Skip one `:' for each `?' */ + if (substr[i] == ':' && skipcol) + { + skipcol--; + i++; + continue; + } + if (substr[i] == delim) + break; + if (substr[i] == '?') + { + skipcol++; + i++; + continue; + } + ADVANCE_CHAR (substr, sublen, i); + } + + return (substr + i); +} + +/* Verify and limit the start and end of the desired substring. If + VTYPE == 0, a regular shell variable is being used; if it is 1, + then the positional parameters are being used; if it is 2, then + VALUE is really a pointer to an array variable that should be used. + Return value is 1 if both values were OK, 0 if there was a problem + with an invalid expression, or -1 if the values were out of range. */ +static int +verify_substring_values (v, value, substr, vtype, e1p, e2p) + SHELL_VAR *v; + char *value, *substr; + int vtype; + intmax_t *e1p, *e2p; +{ + char *t, *temp1, *temp2; + arrayind_t len; + int expok; +#if defined (ARRAY_VARS) + ARRAY *a; + HASH_TABLE *h; +#endif + + /* duplicate behavior of strchr(3) */ + t = skiparith (substr, ':'); + if (*t && *t == ':') + *t = '\0'; + else + t = (char *)0; + + temp1 = expand_arith_string (substr, Q_DOUBLE_QUOTES); + *e1p = evalexp (temp1, &expok); + free (temp1); + if (expok == 0) + return (0); + + len = -1; /* paranoia */ + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + len = MB_STRLEN (value); + break; + case VT_POSPARMS: + len = number_of_args () + 1; + if (*e1p == 0) + len++; /* add one arg if counting from $0 */ + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + /* For arrays, the first value deals with array indices. Negative + offsets count from one past the array's maximum index. Associative + arrays treat the number of elements as the maximum index. */ + if (assoc_p (v)) + { + h = assoc_cell (v); + len = assoc_num_elements (h) + (*e1p < 0); + } + else + { + a = (ARRAY *)value; + len = array_max_index (a) + (*e1p < 0); /* arrays index from 0 to n - 1 */ + } + break; +#endif + } + + if (len == -1) /* paranoia */ + return -1; + + if (*e1p < 0) /* negative offsets count from end */ + *e1p += len; + + if (*e1p > len || *e1p < 0) + return (-1); + +#if defined (ARRAY_VARS) + /* For arrays, the second offset deals with the number of elements. */ + if (vtype == VT_ARRAYVAR) + len = assoc_p (v) ? assoc_num_elements (h) : array_num_elements (a); +#endif + + if (t) + { + t++; + temp2 = savestring (t); + temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES); + free (temp2); + t[-1] = ':'; + *e2p = evalexp (temp1, &expok); + free (temp1); + if (expok == 0) + return (0); + if ((vtype == VT_ARRAYVAR || vtype == VT_POSPARMS) && *e2p < 0) + { + internal_error (_("%s: substring expression < 0"), t); + return (0); + } +#if defined (ARRAY_VARS) + /* In order to deal with sparse arrays, push the intelligence about how + to deal with the number of elements desired down to the array- + specific functions. */ + if (vtype != VT_ARRAYVAR) +#endif + { + if (*e2p < 0) + { + *e2p += len; + if (*e2p < 0 || *e2p < *e1p) + { + internal_error (_("%s: substring expression < 0"), t); + return (0); + } + } + else + *e2p += *e1p; /* want E2 chars starting at E1 */ + if (*e2p > len) + *e2p = len; + } + } + else + *e2p = len; + + return (1); +} + +/* Return the type of variable specified by VARNAME (simple variable, + positional param, or array variable). Also return the value specified + by VARNAME (value of a variable or a reference to an array element). + QUOTED is the standard description of quoting state, using Q_* defines. + FLAGS is currently a set of flags to pass to array_value. If IND is + non-null and not INTMAX_MIN, and FLAGS includes AV_USEIND, IND is + passed to array_value so the array index is not computed again. + If this returns VT_VARIABLE, the caller assumes that CTLESC and CTLNUL + characters in the value are quoted with CTLESC and takes appropriate + steps. For convenience, *VALP is set to the dequoted VALUE. */ +static int +get_var_and_type (varname, value, ind, quoted, flags, varp, valp) + char *varname, *value; + arrayind_t ind; + int quoted, flags; + SHELL_VAR **varp; + char **valp; +{ + int vtype; + char *temp; +#if defined (ARRAY_VARS) + SHELL_VAR *v; +#endif + arrayind_t lind; + + /* This sets vtype to VT_VARIABLE or VT_POSPARMS */ + vtype = (varname[0] == '@' || varname[0] == '*') && varname[1] == '\0'; + if (vtype == VT_POSPARMS && varname[0] == '*') + vtype |= VT_STARSUB; + *varp = (SHELL_VAR *)NULL; + +#if defined (ARRAY_VARS) + if (valid_array_reference (varname)) + { + v = array_variable_part (varname, &temp, (int *)0); + /* If we want to signal array_value to use an already-computed index, + set LIND to that index */ + lind = (ind != INTMAX_MIN && (flags & AV_USEIND)) ? ind : 0; + if (v && (array_p (v) || assoc_p (v))) + { /* [ */ + if (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']') + { + /* Callers have to differentiate betwen indexed and associative */ + vtype = VT_ARRAYVAR; + if (temp[0] == '*') + vtype |= VT_STARSUB; + *valp = array_p (v) ? (char *)array_cell (v) : (char *)assoc_cell (v); + } + else + { + vtype = VT_ARRAYMEMBER; + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); + } + *varp = v; + } + else if (v && (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']')) + { + vtype = VT_VARIABLE; + *varp = v; + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + *valp = dequote_string (value); + else + *valp = dequote_escapes (value); + } + else + { + vtype = VT_ARRAYMEMBER; + *varp = v; + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); + } + } + else if ((v = find_variable (varname)) && (invisible_p (v) == 0) && (assoc_p (v) || array_p (v))) + { + vtype = VT_ARRAYMEMBER; + *varp = v; + *valp = assoc_p (v) ? assoc_reference (assoc_cell (v), "0") : array_reference (array_cell (v), 0); + } + else +#endif + { + if (value && vtype == VT_VARIABLE) + { + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + *valp = dequote_string (value); + else + *valp = dequote_escapes (value); + } + else + *valp = value; + } + + return vtype; +} + +/******************************************************/ +/* */ +/* Functions to extract substrings of variable values */ +/* */ +/******************************************************/ + +#if defined (HANDLE_MULTIBYTE) +/* Character-oriented rather than strictly byte-oriented substrings. S and + E, rather being strict indices into STRING, indicate character (possibly + multibyte character) positions that require calculation. + Used by the ${param:offset[:length]} expansion. */ +static char * +mb_substring (string, s, e) + char *string; + int s, e; +{ + char *tt; + int start, stop, i, slen; + DECLARE_MBSTATE; + + start = 0; + /* Don't need string length in ADVANCE_CHAR unless multibyte chars possible. */ + slen = (MB_CUR_MAX > 1) ? STRLEN (string) : 0; + + i = s; + while (string[start] && i--) + ADVANCE_CHAR (string, slen, start); + stop = start; + i = e - s; + while (string[stop] && i--) + ADVANCE_CHAR (string, slen, stop); + tt = substring (string, start, stop); + return tt; +} +#endif + +/* Process a variable substring expansion: ${name:e1[:e2]}. If VARNAME + is `@', use the positional parameters; otherwise, use the value of + VARNAME. If VARNAME is an array variable, use the array elements. */ + +static char * +parameter_brace_substring (varname, value, ind, substr, quoted, flags) + char *varname, *value; + int ind; + char *substr; + int quoted, flags; +{ + intmax_t e1, e2; + int vtype, r, starsub; + char *temp, *val, *tt, *oname; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + oname = this_command_name; + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + { + this_command_name = oname; + return ((char *)NULL); + } + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + r = verify_substring_values (v, val, substr, vtype, &e1, &e2); + this_command_name = oname; + if (r <= 0) + return ((r == 0) ? &expand_param_error : (char *)NULL); + + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + tt = mb_substring (val, e1, e2); + else +#endif + tt = substring (val, e1, e2); + + if (vtype == VT_VARIABLE) + FREE (val); + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + temp = quote_string (tt); + else + temp = tt ? quote_escapes (tt) : (char *)NULL; + FREE (tt); + break; + case VT_POSPARMS: + tt = pos_params (varname, e1, e2, quoted); + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0) + { + temp = tt ? quote_escapes (tt) : (char *)NULL; + FREE (tt); + } + else + temp = tt; + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + if (assoc_p (v)) + /* we convert to list and take first e2 elements starting at e1th + element -- officially undefined for now */ + temp = assoc_subrange (assoc_cell (v), e1, e2, starsub, quoted); + else + /* We want E2 to be the number of elements desired (arrays can be sparse, + so verify_substring_values just returns the numbers specified and we + rely on array_subrange to understand how to deal with them). */ + temp = array_subrange (array_cell (v), e1, e2, starsub, quoted); + /* array_subrange now calls array_quote_escapes as appropriate, so the + caller no longer needs to. */ + break; +#endif + default: + temp = (char *)NULL; + } + + return temp; +} + +/****************************************************************/ +/* */ +/* Functions to perform pattern substitution on variable values */ +/* */ +/****************************************************************/ + +static int +shouldexp_replacement (s) + char *s; +{ + register char *p; + + for (p = s; p && *p; p++) + { + if (*p == '\\') + p++; + else if (*p == '&') + return 1; + } + return 0; +} + +char * +pat_subst (string, pat, rep, mflags) + char *string, *pat, *rep; + int mflags; +{ + char *ret, *s, *e, *str, *rstr, *mstr; + int rsize, rptr, l, replen, mtype, rxpand, rslen, mlen; + + if (string == 0) + return (savestring ("")); + + mtype = mflags & MATCH_TYPEMASK; + +#if 0 /* bash-4.2 ? */ + rxpand = (rep && *rep) ? shouldexp_replacement (rep) : 0; +#else + rxpand = 0; +#endif + + /* Special cases: + * 1. A null pattern with mtype == MATCH_BEG means to prefix STRING + * with REP and return the result. + * 2. A null pattern with mtype == MATCH_END means to append REP to + * STRING and return the result. + * These don't understand or process `&' in the replacement string. + */ + if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END)) + { + replen = STRLEN (rep); + l = STRLEN (string); + ret = (char *)xmalloc (replen + l + 2); + if (replen == 0) + strcpy (ret, string); + else if (mtype == MATCH_BEG) + { + strcpy (ret, rep); + strcpy (ret + replen, string); + } + else + { + strcpy (ret, string); + strcpy (ret + l, rep); + } + return (ret); + } + + ret = (char *)xmalloc (rsize = 64); + ret[0] = '\0'; + + for (replen = STRLEN (rep), rptr = 0, str = string;;) + { + if (match_pattern (str, pat, mtype, &s, &e) == 0) + break; + l = s - str; + + if (rxpand) + { + int x; + mlen = e - s; + mstr = xmalloc (mlen + 1); + for (x = 0; x < mlen; x++) + mstr[x] = s[x]; + mstr[mlen] = '\0'; + rstr = strcreplace (rep, '&', mstr, 0); + rslen = strlen (rstr); + } + else + { + rstr = rep; + rslen = replen; + } + + RESIZE_MALLOCED_BUFFER (ret, rptr, (l + rslen), rsize, 64); + + /* OK, now copy the leading unmatched portion of the string (from + str to s) to ret starting at rptr (the current offset). Then copy + the replacement string at ret + rptr + (s - str). Increment + rptr (if necessary) and str and go on. */ + if (l) + { + strncpy (ret + rptr, str, l); + rptr += l; + } + if (replen) + { + strncpy (ret + rptr, rstr, rslen); + rptr += rslen; + } + str = e; /* e == end of match */ + + if (rstr != rep) + free (rstr); + + if (((mflags & MATCH_GLOBREP) == 0) || mtype != MATCH_ANY) + break; + + if (s == e) + { + /* On a zero-length match, make sure we copy one character, since + we increment one character to avoid infinite recursion. */ + RESIZE_MALLOCED_BUFFER (ret, rptr, 1, rsize, 64); + ret[rptr++] = *str++; + e++; /* avoid infinite recursion on zero-length match */ + } + } + + /* Now copy the unmatched portion of the input string */ + if (str && *str) + { + RESIZE_MALLOCED_BUFFER (ret, rptr, STRLEN(str) + 1, rsize, 64); + strcpy (ret + rptr, str); + } + else + ret[rptr] = '\0'; + + return ret; +} + +/* Do pattern match and replacement on the positional parameters. */ +static char * +pos_params_pat_subst (string, pat, rep, mflags) + char *string, *pat, *rep; + int mflags; +{ + WORD_LIST *save, *params; + WORD_DESC *w; + char *ret; + int pchar, qflags; + + save = params = list_rest_of_args (); + if (save == 0) + return ((char *)NULL); + + for ( ; params; params = params->next) + { + ret = pat_subst (params->word->word, pat, rep, mflags); + w = alloc_word_desc (); + w->word = ret ? ret : savestring (""); + dispose_word (params->word); + params->word = w; + } + + pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@'; + qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0; + +#if 0 + if ((mflags & (MATCH_QUOTED|MATCH_STARSUB)) == (MATCH_QUOTED|MATCH_STARSUB)) + ret = string_list_dollar_star (quote_list (save)); + else if ((mflags & MATCH_STARSUB) == MATCH_STARSUB) + ret = string_list_dollar_star (save); + else if ((mflags & MATCH_QUOTED) == MATCH_QUOTED) + ret = string_list_dollar_at (save, qflags); + else + ret = string_list_dollar_star (save); +#else + ret = string_list_pos_params (pchar, save, qflags); +#endif + + dispose_words (save); + + return (ret); +} + +/* Perform pattern substitution on VALUE, which is the expansion of + VARNAME. PATSUB is an expression supplying the pattern to match + and the string to substitute. QUOTED is a flags word containing + the type of quoting currently in effect. */ +static char * +parameter_brace_patsub (varname, value, ind, patsub, quoted, flags) + char *varname, *value; + int ind; + char *patsub; + int quoted, flags; +{ + int vtype, mflags, starsub, delim; + char *val, *temp, *pat, *rep, *p, *lpatsub, *tt; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + return ((char *)NULL); + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + mflags = 0; + if (patsub && *patsub == '/') + { + mflags |= MATCH_GLOBREP; + patsub++; + } + + /* Malloc this because expand_string_if_necessary or one of the expansion + functions in its call chain may free it on a substitution error. */ + lpatsub = savestring (patsub); + + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + mflags |= MATCH_QUOTED; + + if (starsub) + mflags |= MATCH_STARSUB; + + /* If the pattern starts with a `/', make sure we skip over it when looking + for the replacement delimiter. */ +#if 0 + if (rep = quoted_strchr ((*patsub == '/') ? lpatsub+1 : lpatsub, '/', ST_BACKSL)) + *rep++ = '\0'; + else + rep = (char *)NULL; +#else + delim = skip_to_delim (lpatsub, ((*patsub == '/') ? 1 : 0), "/", 0); + if (lpatsub[delim] == '/') + { + lpatsub[delim] = 0; + rep = lpatsub + delim + 1; + } + else + rep = (char *)NULL; +#endif + + if (rep && *rep == '\0') + rep = (char *)NULL; + + /* Perform the same expansions on the pattern as performed by the + pattern removal expansions. */ + pat = getpattern (lpatsub, quoted, 1); + + if (rep) + { + if ((mflags & MATCH_QUOTED) == 0) + rep = expand_string_if_necessary (rep, quoted, expand_string_unsplit); + else + rep = expand_string_to_string_internal (rep, quoted, expand_string_unsplit); + } + + /* ksh93 doesn't allow the match specifier to be a part of the expanded + pattern. This is an extension. Make sure we don't anchor the pattern + at the beginning or end of the string if we're doing global replacement, + though. */ + p = pat; + if (mflags & MATCH_GLOBREP) + mflags |= MATCH_ANY; + else if (pat && pat[0] == '#') + { + mflags |= MATCH_BEG; + p++; + } + else if (pat && pat[0] == '%') + { + mflags |= MATCH_END; + p++; + } + else + mflags |= MATCH_ANY; + + /* OK, we now want to substitute REP for PAT in VAL. If + flags & MATCH_GLOBREP is non-zero, the substitution is done + everywhere, otherwise only the first occurrence of PAT is + replaced. The pattern matching code doesn't understand + CTLESC quoting CTLESC and CTLNUL so we use the dequoted variable + values passed in (VT_VARIABLE) so the pattern substitution + code works right. We need to requote special chars after + we're done for VT_VARIABLE and VT_ARRAYMEMBER, and for the + other cases if QUOTED == 0, since the posparams and arrays + indexed by * or @ do special things when QUOTED != 0. */ + + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + temp = pat_subst (val, p, rep, mflags); + if (vtype == VT_VARIABLE) + FREE (val); + if (temp) + { + tt = (mflags & MATCH_QUOTED) ? quote_string (temp) : quote_escapes (temp); + free (temp); + temp = tt; + } + break; + case VT_POSPARMS: + temp = pos_params_pat_subst (val, p, rep, mflags); + if (temp && (mflags & MATCH_QUOTED) == 0) + { + tt = quote_escapes (temp); + free (temp); + temp = tt; + } + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + temp = assoc_p (v) ? assoc_patsub (assoc_cell (v), p, rep, mflags) + : array_patsub (array_cell (v), p, rep, mflags); + /* Don't call quote_escapes anymore; array_patsub calls + array_quote_escapes as appropriate before adding the + space separators; ditto for assoc_patsub. */ + break; +#endif + } + + FREE (pat); + FREE (rep); + free (lpatsub); + + return temp; +} + +/****************************************************************/ +/* */ +/* Functions to perform case modification on variable values */ +/* */ +/****************************************************************/ + +/* Do case modification on the positional parameters. */ + +static char * +pos_params_modcase (string, pat, modop, mflags) + char *string, *pat; + int modop; + int mflags; +{ + WORD_LIST *save, *params; + WORD_DESC *w; + char *ret; + int pchar, qflags; + + save = params = list_rest_of_args (); + if (save == 0) + return ((char *)NULL); + + for ( ; params; params = params->next) + { + ret = sh_modcase (params->word->word, pat, modop); + w = alloc_word_desc (); + w->word = ret ? ret : savestring (""); + dispose_word (params->word); + params->word = w; + } + + pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@'; + qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0; + + ret = string_list_pos_params (pchar, save, qflags); + dispose_words (save); + + return (ret); +} + +/* Perform case modification on VALUE, which is the expansion of + VARNAME. MODSPEC is an expression supplying the type of modification + to perform. QUOTED is a flags word containing the type of quoting + currently in effect. */ +static char * +parameter_brace_casemod (varname, value, ind, modspec, patspec, quoted, flags) + char *varname, *value; + int ind, modspec; + char *patspec; + int quoted, flags; +{ + int vtype, starsub, modop, mflags, x; + char *val, *temp, *pat, *p, *lpat, *tt; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + return ((char *)NULL); + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + modop = 0; + mflags = 0; + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + mflags |= MATCH_QUOTED; + if (starsub) + mflags |= MATCH_STARSUB; + + p = patspec; + if (modspec == '^') + { + x = p && p[0] == modspec; + modop = x ? CASE_UPPER : CASE_UPFIRST; + p += x; + } + else if (modspec == ',') + { + x = p && p[0] == modspec; + modop = x ? CASE_LOWER : CASE_LOWFIRST; + p += x; + } + else if (modspec == '~') + { + x = p && p[0] == modspec; + modop = x ? CASE_TOGGLEALL : CASE_TOGGLE; + p += x; + } + + lpat = p ? savestring (p) : 0; + /* Perform the same expansions on the pattern as performed by the + pattern removal expansions. FOR LATER */ + pat = lpat ? getpattern (lpat, quoted, 1) : 0; + + /* OK, now we do the case modification. */ + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + temp = sh_modcase (val, pat, modop); + if (vtype == VT_VARIABLE) + FREE (val); + if (temp) + { + tt = (mflags & MATCH_QUOTED) ? quote_string (temp) : quote_escapes (temp); + free (temp); + temp = tt; + } + break; + + case VT_POSPARMS: + temp = pos_params_modcase (val, pat, modop, mflags); + if (temp && (mflags & MATCH_QUOTED) == 0) + { + tt = quote_escapes (temp); + free (temp); + temp = tt; + } + break; + +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + temp = assoc_p (v) ? assoc_modcase (assoc_cell (v), pat, modop, mflags) + : array_modcase (array_cell (v), pat, modop, mflags); + /* Don't call quote_escapes; array_modcase calls array_quote_escapes + as appropriate before adding the space separators; ditto for + assoc_modcase. */ + break; +#endif + } + + FREE (pat); + free (lpat); + + return temp; +} + +/* Check for unbalanced parens in S, which is the contents of $(( ... )). If + any occur, this must be a nested command substitution, so return 0. + Otherwise, return 1. A valid arithmetic expression must always have a + ( before a matching ), so any cases where there are more right parens + means that this must not be an arithmetic expression, though the parser + will not accept it without a balanced total number of parens. */ +static int +chk_arithsub (s, len) + const char *s; + int len; +{ + int i, count; + DECLARE_MBSTATE; + + i = count = 0; + while (i < len) + { + if (s[i] == LPAREN) + count++; + else if (s[i] == RPAREN) + { + count--; + if (count < 0) + return 0; + } + + switch (s[i]) + { + default: + ADVANCE_CHAR (s, len, i); + break; + + case '\\': + i++; + if (s[i]) + ADVANCE_CHAR (s, len, i); + break; + + case '\'': + i = skip_single_quoted (s, len, ++i); + break; + + case '"': + i = skip_double_quoted ((char *)s, len, ++i); + break; + } + } + + return (count == 0); +} + +/****************************************************************/ +/* */ +/* Functions to perform parameter expansion on a string */ +/* */ +/****************************************************************/ + +/* ${[#][!]name[[:][^[^]][,[,]]#[#]%[%]-=?+[word][:e1[:e2]]]} */ +static WORD_DESC * +parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, contains_dollar_at) + char *string; + int *indexp, quoted, *quoted_dollar_atp, *contains_dollar_at, pflags; +{ + int check_nullness, var_is_set, var_is_null, var_is_special; + int want_substring, want_indir, want_patsub, want_casemod; + char *name, *value, *temp, *temp1; + WORD_DESC *tdesc, *ret; + int t_index, sindex, c, tflag, modspec; + intmax_t number; + arrayind_t ind; + + temp = temp1 = value = (char *)NULL; + var_is_set = var_is_null = var_is_special = check_nullness = 0; + want_substring = want_indir = want_patsub = want_casemod = 0; + + sindex = *indexp; + t_index = ++sindex; + /* ${#var} doesn't have any of the other parameter expansions on it. */ + if (string[t_index] == '#' && legal_variable_starter (string[t_index+1])) /* {{ */ + name = string_extract (string, &t_index, "}", SX_VARNAME); + else +#if defined (CASEMOD_EXPANSIONS) + /* To enable case-toggling expansions using the `~' operator character + change the 1 to 0. */ +# if defined (CASEMOD_CAPCASE) + name = string_extract (string, &t_index, "#%^,~:-=?+/}", SX_VARNAME); +# else + name = string_extract (string, &t_index, "#%^,:-=?+/}", SX_VARNAME); +# endif /* CASEMOD_CAPCASE */ +#else + name = string_extract (string, &t_index, "#%:-=?+/}", SX_VARNAME); +#endif /* CASEMOD_EXPANSIONS */ + + ret = 0; + tflag = 0; + + ind = INTMAX_MIN; + + /* If the name really consists of a special variable, then make sure + that we have the entire name. We don't allow indirect references + to special variables except `#', `?', `@' and `*'. */ + if ((sindex == t_index && + (string[t_index] == '-' || + string[t_index] == '?' || + string[t_index] == '#')) || + (sindex == t_index - 1 && string[sindex] == '!' && + (string[t_index] == '#' || + string[t_index] == '?' || + string[t_index] == '@' || + string[t_index] == '*'))) + { + t_index++; + free (name); + temp1 = string_extract (string, &t_index, "#%:-=?+/}", 0); + name = (char *)xmalloc (3 + (strlen (temp1))); + *name = string[sindex]; + if (string[sindex] == '!') + { + /* indirect reference of $#, $?, $@, or $* */ + name[1] = string[sindex + 1]; + strcpy (name + 2, temp1); + } + else + strcpy (name + 1, temp1); + free (temp1); + } + sindex = t_index; + + /* Find out what character ended the variable name. Then + do the appropriate thing. */ + if (c = string[sindex]) + sindex++; + + /* If c is followed by one of the valid parameter expansion + characters, move past it as normal. If not, assume that + a substring specification is being given, and do not move + past it. */ + if (c == ':' && VALID_PARAM_EXPAND_CHAR (string[sindex])) + { + check_nullness++; + if (c = string[sindex]) + sindex++; + } + else if (c == ':' && string[sindex] != RBRACE) + want_substring = 1; + else if (c == '/' && string[sindex] != RBRACE) + want_patsub = 1; +#if defined (CASEMOD_EXPANSIONS) + else if (c == '^' || c == ',' || c == '~') + { + modspec = c; + want_casemod = 1; + } +#endif + + /* Catch the valid and invalid brace expressions that made it through the + tests above. */ + /* ${#-} is a valid expansion and means to take the length of $-. + Similarly for ${#?} and ${##}... */ + if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 && + VALID_SPECIAL_LENGTH_PARAM (c) && string[sindex] == RBRACE) + { + name = (char *)xrealloc (name, 3); + name[1] = c; + name[2] = '\0'; + c = string[sindex++]; + } + + /* ...but ${#%}, ${#:}, ${#=}, ${#+}, and ${#/} are errors. */ + if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 && + member (c, "%:=+/") && string[sindex] == RBRACE) + { + temp = (char *)NULL; + goto bad_substitution; + } + + /* Indirect expansion begins with a `!'. A valid indirect expansion is + either a variable name, one of the positional parameters or a special + variable that expands to one of the positional parameters. */ + want_indir = *name == '!' && + (legal_variable_starter ((unsigned char)name[1]) || DIGIT (name[1]) + || VALID_INDIR_PARAM (name[1])); + + /* Determine the value of this variable. */ + + /* Check for special variables, directly referenced. */ + if (SPECIAL_VAR (name, want_indir)) + var_is_special++; + + /* Check for special expansion things, like the length of a parameter */ + if (*name == '#' && name[1]) + { + /* If we are not pointing at the character just after the + closing brace, then we haven't gotten all of the name. + Since it begins with a special character, this is a bad + substitution. Also check NAME for validity before trying + to go on. */ + if (string[sindex - 1] != RBRACE || (valid_length_expression (name) == 0)) + { + temp = (char *)NULL; + goto bad_substitution; + } + + number = parameter_brace_expand_length (name); + free (name); + + *indexp = sindex; + if (number < 0) + return (&expand_wdesc_error); + else + { + ret = alloc_word_desc (); + ret->word = itos (number); + return ret; + } + } + + /* ${@} is identical to $@. */ + if (name[0] == '@' && name[1] == '\0') + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + + if (contains_dollar_at) + *contains_dollar_at = 1; + } + + /* Process ${!PREFIX*} expansion. */ + if (want_indir && string[sindex - 1] == RBRACE && + (string[sindex - 2] == '*' || string[sindex - 2] == '@') && + legal_variable_starter ((unsigned char) name[1])) + { + char **x; + WORD_LIST *xlist; + + temp1 = savestring (name + 1); + number = strlen (temp1); + temp1[number - 1] = '\0'; + x = all_variables_matching_prefix (temp1); + xlist = strvec_to_word_list (x, 0, 0); + if (string[sindex - 2] == '*') + temp = string_list_dollar_star (xlist); + else + { + temp = string_list_dollar_at (xlist, quoted); + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + } + free (x); + dispose_words (xlist); + free (temp1); + *indexp = sindex; + + ret = alloc_word_desc (); + ret->word = temp; + return ret; + } + +#if defined (ARRAY_VARS) + /* Process ${!ARRAY[@]} and ${!ARRAY[*]} expansion. */ /* [ */ + if (want_indir && string[sindex - 1] == RBRACE && + string[sindex - 2] == ']' && valid_array_reference (name+1)) + { + char *x, *x1; + + temp1 = savestring (name + 1); + x = array_variable_name (temp1, &x1, (int *)0); /* [ */ + FREE (x); + if (ALL_ELEMENT_SUB (x1[0]) && x1[1] == ']') + { + temp = array_keys (temp1, quoted); /* handles assoc vars too */ + if (x1[0] == '@') + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + } + + free (temp1); + *indexp = sindex; + + ret = alloc_word_desc (); + ret->word = temp; + return ret; + } + + free (temp1); + } +#endif /* ARRAY_VARS */ + + /* Make sure that NAME is valid before trying to go on. */ + if (valid_brace_expansion_word (want_indir ? name + 1 : name, + var_is_special) == 0) + { + temp = (char *)NULL; + goto bad_substitution; + } + + if (want_indir) + tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at); + else + tdesc = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND|(pflags&PF_NOSPLIT2), &ind); + + if (tdesc) + { + temp = tdesc->word; + tflag = tdesc->flags; + dispose_word_desc (tdesc); + } + else + temp = (char *)0; + +#if defined (ARRAY_VARS) + if (valid_array_reference (name)) + chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at); +#endif + + var_is_set = temp != (char *)0; + var_is_null = check_nullness && (var_is_set == 0 || *temp == 0); + + /* Get the rest of the stuff inside the braces. */ + if (c && c != RBRACE) + { + /* Extract the contents of the ${ ... } expansion + according to the Posix.2 rules. */ + value = extract_dollar_brace_string (string, &sindex, quoted, (c == '%' || c == '#') ? SX_POSIXEXP : 0); + if (string[sindex] == RBRACE) + sindex++; + else + goto bad_substitution; + } + else + value = (char *)NULL; + + *indexp = sindex; + + /* If this is a substring spec, process it and add the result. */ + if (want_substring) + { + temp1 = parameter_brace_substring (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + FREE (name); + FREE (value); + FREE (temp); + + if (temp1 == &expand_param_error) + return (&expand_wdesc_error); + else if (temp1 == &expand_param_fatal) + return (&expand_wdesc_fatal); + + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + } + else if (want_patsub) + { + temp1 = parameter_brace_patsub (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + FREE (name); + FREE (value); + FREE (temp); + + if (temp1 == &expand_param_error) + return (&expand_wdesc_error); + else if (temp1 == &expand_param_fatal) + return (&expand_wdesc_fatal); + + ret = alloc_word_desc (); + ret->word = temp1; + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + } +#if defined (CASEMOD_EXPANSIONS) + else if (want_casemod) + { + temp1 = parameter_brace_casemod (name, temp, ind, modspec, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + FREE (name); + FREE (value); + FREE (temp); + + if (temp1 == &expand_param_error) + return (&expand_wdesc_error); + else if (temp1 == &expand_param_fatal) + return (&expand_wdesc_fatal); + + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + } +#endif + + /* Do the right thing based on which character ended the variable name. */ + switch (c) + { + default: + case '\0': + bad_substitution: + report_error (_("%s: bad substitution"), string ? string : "??"); + FREE (value); + FREE (temp); + free (name); + return &expand_wdesc_error; + + case RBRACE: + if (var_is_set == 0 && unbound_vars_is_error && ((name[0] != '@' && name[0] != '*') || name[1])) + { + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (name); + FREE (value); + FREE (temp); + free (name); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + break; + + case '#': /* ${param#[#]pattern} */ + case '%': /* ${param%[%]pattern} */ + if (value == 0 || *value == '\0' || temp == 0 || *temp == '\0') + { + FREE (value); + break; + } + temp1 = parameter_brace_remove_pattern (name, temp, ind, value, c, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + free (temp); + free (value); + free (name); + + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + + case '-': + case '=': + case '?': + case '+': + if (var_is_set && var_is_null == 0) + { + /* If the operator is `+', we don't want the value of the named + variable for anything, just the value of the right hand side. */ + if (c == '+') + { + /* XXX -- if we're double-quoted and the named variable is "$@", + we want to turn off any special handling of "$@" -- + we're not using it, so whatever is on the rhs applies. */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 0; + if (contains_dollar_at) + *contains_dollar_at = 0; + + FREE (temp); + if (value) + { + /* From Posix discussion on austin-group list. Issue 221 + requires that backslashes escaping `}' inside + double-quoted ${...} be removed. */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + quoted |= Q_DOLBRACE; + ret = parameter_brace_expand_rhs (name, value, c, + quoted, + quoted_dollar_atp, + contains_dollar_at); + /* XXX - fix up later, esp. noting presence of + W_HASQUOTEDNULL in ret->flags */ + free (value); + } + else + temp = (char *)NULL; + } + else + { + FREE (value); + } + /* Otherwise do nothing; just use the value in TEMP. */ + } + else /* VAR not set or VAR is NULL. */ + { + FREE (temp); + temp = (char *)NULL; + if (c == '=' && var_is_special) + { + report_error (_("$%s: cannot assign in this way"), name); + free (name); + free (value); + return &expand_wdesc_error; + } + else if (c == '?') + { + parameter_brace_expand_error (name, value); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + else if (c != '+') + { + /* XXX -- if we're double-quoted and the named variable is "$@", + we want to turn off any special handling of "$@" -- + we're not using it, so whatever is on the rhs applies. */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 0; + if (contains_dollar_at) + *contains_dollar_at = 0; + + /* From Posix discussion on austin-group list. Issue 221 requires + that backslashes escaping `}' inside double-quoted ${...} be + removed. */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + quoted |= Q_DOLBRACE; + ret = parameter_brace_expand_rhs (name, value, c, quoted, + quoted_dollar_atp, + contains_dollar_at); + /* XXX - fix up later, esp. noting presence of + W_HASQUOTEDNULL in tdesc->flags */ + } + free (value); + } + + break; + } + free (name); + + if (ret == 0) + { + ret = alloc_word_desc (); + ret->flags = tflag; + ret->word = temp; + } + return (ret); +} + +/* Expand a single ${xxx} expansion. The braces are optional. When + the braces are used, parameter_brace_expand() does the work, + possibly calling param_expand recursively. */ +static WORD_DESC * +param_expand (string, sindex, quoted, expanded_something, + contains_dollar_at, quoted_dollar_at_p, had_quoted_null_p, + pflags) + char *string; + int *sindex, quoted, *expanded_something, *contains_dollar_at; + int *quoted_dollar_at_p, *had_quoted_null_p, pflags; +{ + char *temp, *temp1, uerror[3]; + int zindex, t_index, expok; + unsigned char c; + intmax_t number; + SHELL_VAR *var; + WORD_LIST *list; + WORD_DESC *tdesc, *ret; + int tflag; + + zindex = *sindex; + c = string[++zindex]; + + temp = (char *)NULL; + ret = tdesc = (WORD_DESC *)NULL; + tflag = 0; + + /* Do simple cases first. Switch on what follows '$'. */ + switch (c) + { + /* $0 .. $9? */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + temp1 = dollar_vars[TODIGIT (c)]; + if (unbound_vars_is_error && temp1 == (char *)NULL) + { + uerror[0] = '$'; + uerror[1] = c; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + if (temp1) + temp = (*temp1 && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ? quote_string (temp1) + : quote_escapes (temp1); + else + temp = (char *)NULL; + + break; + + /* $$ -- pid of the invoking shell. */ + case '$': + temp = itos (dollar_dollar_pid); + break; + + /* $# -- number of positional parameters. */ + case '#': + temp = itos (number_of_args ()); + break; + + /* $? -- return value of the last synchronous command. */ + case '?': + temp = itos (last_command_exit_value); + break; + + /* $- -- flags supplied to the shell on invocation or by `set'. */ + case '-': + temp = which_set_flags (); + break; + + /* $! -- Pid of the last asynchronous command. */ + case '!': + /* If no asynchronous pids have been created, expand to nothing. + If `set -u' has been executed, and no async processes have + been created, this is an expansion error. */ + if (last_asynchronous_pid == NO_PID) + { + if (expanded_something) + *expanded_something = 0; + temp = (char *)NULL; + if (unbound_vars_is_error) + { + uerror[0] = '$'; + uerror[1] = c; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + } + else + temp = itos (last_asynchronous_pid); + break; + + /* The only difference between this and $@ is when the arg is quoted. */ + case '*': /* `$*' */ + list = list_rest_of_args (); + +#if 0 + /* According to austin-group posix proposal by Geoff Clare in + <20090505091501.GA10097@squonk.masqnet> of 5 May 2009: + + "The shell shall write a message to standard error and + immediately exit when it tries to expand an unset parameter + other than the '@' and '*' special parameters." + */ + + if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0) + { + uerror[0] = '$'; + uerror[1] = '*'; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } +#endif + + /* If there are no command-line arguments, this should just + disappear if there are other characters in the expansion, + even if it's quoted. */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && list == 0) + temp = (char *)NULL; + else if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE)) + { + /* If we have "$*" we want to make a string of the positional + parameters, separated by the first character of $IFS, and + quote the whole string, including the separators. If IFS + is unset, the parameters are separated by ' '; if $IFS is + null, the parameters are concatenated. */ + temp = (quoted & (Q_DOUBLE_QUOTES|Q_PATQUOTE)) ? string_list_dollar_star (list) : string_list (list); + temp1 = quote_string (temp); + if (*temp == 0) + tflag |= W_HASQUOTEDNULL; + free (temp); + temp = temp1; + } + else + { + /* We check whether or not we're eventually going to split $* here, + for example when IFS is empty and we are processing the rhs of + an assignment statement. In that case, we don't separate the + arguments at all. Otherwise, if the $* is not quoted it is + identical to $@ */ +#if 1 +# if defined (HANDLE_MULTIBYTE) + if (expand_no_split_dollar_star && ifs_firstc[0] == 0) +# else + if (expand_no_split_dollar_star && ifs_firstc == 0) +# endif + temp = string_list_dollar_star (list); + else + temp = string_list_dollar_at (list, quoted); +#else + temp = string_list_dollar_at (list, quoted); +#endif + if (expand_no_split_dollar_star == 0 && contains_dollar_at) + *contains_dollar_at = 1; + } + + dispose_words (list); + break; + + /* When we have "$@" what we want is "$1" "$2" "$3" ... This + means that we have to turn quoting off after we split into + the individually quoted arguments so that the final split + on the first character of $IFS is still done. */ + case '@': /* `$@' */ + list = list_rest_of_args (); + +#if 0 + /* According to austin-group posix proposal by Geoff Clare in + <20090505091501.GA10097@squonk.masqnet> of 5 May 2009: + + "The shell shall write a message to standard error and + immediately exit when it tries to expand an unset parameter + other than the '@' and '*' special parameters." + */ + + if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0) + { + uerror[0] = '$'; + uerror[1] = '@'; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } +#endif + + /* We want to flag the fact that we saw this. We can't turn + off quoting entirely, because other characters in the + string might need it (consider "\"$@\""), but we need some + way to signal that the final split on the first character + of $IFS should be done, even though QUOTED is 1. */ + /* XXX - should this test include Q_PATQUOTE? */ + if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + *quoted_dollar_at_p = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + +#if 0 + if (pflags & PF_NOSPLIT2) + temp = string_list_internal (quoted ? quote_list (list) : list, " "); + else +#endif + /* We want to separate the positional parameters with the first + character of $IFS in case $IFS is something other than a space. + We also want to make sure that splitting is done no matter what -- + according to POSIX.2, this expands to a list of the positional + parameters no matter what IFS is set to. */ + temp = string_list_dollar_at (list, quoted); + + dispose_words (list); + break; + + case LBRACE: + tdesc = parameter_brace_expand (string, &zindex, quoted, pflags, + quoted_dollar_at_p, + contains_dollar_at); + + if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal) + return (tdesc); + temp = tdesc ? tdesc->word : (char *)0; + + /* XXX */ + /* Quoted nulls should be removed if there is anything else + in the string. */ + /* Note that we saw the quoted null so we can add one back at + the end of this function if there are no other characters + in the string, discard TEMP, and go on. The exception to + this is when we have "${@}" and $1 is '', since $@ needs + special handling. */ + if (tdesc && tdesc->word && (tdesc->flags & W_HASQUOTEDNULL) && QUOTED_NULL (temp)) + { + if (had_quoted_null_p) + *had_quoted_null_p = 1; + if (*quoted_dollar_at_p == 0) + { + free (temp); + tdesc->word = temp = (char *)NULL; + } + + } + + ret = tdesc; + goto return0; + + /* Do command or arithmetic substitution. */ + case LPAREN: + /* We have to extract the contents of this paren substitution. */ + t_index = zindex + 1; + temp = extract_command_subst (string, &t_index, 0); + zindex = t_index; + + /* For Posix.2-style `$(( ))' arithmetic substitution, + extract the expression and pass it to the evaluator. */ + if (temp && *temp == LPAREN) + { + char *temp2; + temp1 = temp + 1; + temp2 = savestring (temp1); + t_index = strlen (temp2) - 1; + + if (temp2[t_index] != RPAREN) + { + free (temp2); + goto comsub; + } + + /* Cut off ending `)' */ + temp2[t_index] = '\0'; + + if (chk_arithsub (temp2, t_index) == 0) + { + free (temp2); +#if 0 + internal_warning (_("future versions of the shell will force evaluation as an arithmetic substitution")); +#endif + goto comsub; + } + + /* Expand variables found inside the expression. */ + temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES); + free (temp2); + +arithsub: + /* No error messages. */ + this_command_name = (char *)NULL; + number = evalexp (temp1, &expok); + free (temp); + free (temp1); + if (expok == 0) + { + if (interactive_shell == 0 && posixly_correct) + { + last_command_exit_value = EXECUTION_FAILURE; + return (&expand_wdesc_fatal); + } + else + return (&expand_wdesc_error); + } + temp = itos (number); + break; + } + +comsub: + if (pflags & PF_NOCOMSUB) + /* we need zindex+1 because string[zindex] == RPAREN */ + temp1 = substring (string, *sindex, zindex+1); + else + { + tdesc = command_substitute (temp, quoted); + temp1 = tdesc ? tdesc->word : (char *)NULL; + if (tdesc) + dispose_word_desc (tdesc); + } + FREE (temp); + temp = temp1; + break; + + /* Do POSIX.2d9-style arithmetic substitution. This will probably go + away in a future bash release. */ + case '[': + /* Extract the contents of this arithmetic substitution. */ + t_index = zindex + 1; + temp = extract_arithmetic_subst (string, &t_index); + zindex = t_index; + if (temp == 0) + { + temp = savestring (string); + if (expanded_something) + *expanded_something = 0; + goto return0; + } + + /* Do initial variable expansion. */ + temp1 = expand_arith_string (temp, Q_DOUBLE_QUOTES); + + goto arithsub; + + default: + /* Find the variable in VARIABLE_LIST. */ + temp = (char *)NULL; + + for (t_index = zindex; (c = string[zindex]) && legal_variable_char (c); zindex++) + ; + temp1 = (zindex > t_index) ? substring (string, t_index, zindex) : (char *)NULL; + + /* If this isn't a variable name, then just output the `$'. */ + if (temp1 == 0 || *temp1 == '\0') + { + FREE (temp1); + temp = (char *)xmalloc (2); + temp[0] = '$'; + temp[1] = '\0'; + if (expanded_something) + *expanded_something = 0; + goto return0; + } + + /* If the variable exists, return its value cell. */ + var = find_variable (temp1); + + if (var && invisible_p (var) == 0 && var_isset (var)) + { +#if defined (ARRAY_VARS) + if (assoc_p (var) || array_p (var)) + { + temp = array_p (var) ? array_reference (array_cell (var), 0) + : assoc_reference (assoc_cell (var), "0"); + if (temp) + temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ? quote_string (temp) + : quote_escapes (temp); + else if (unbound_vars_is_error) + goto unbound_variable; + } + else +#endif + { + temp = value_cell (var); + + temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ? quote_string (temp) + : quote_escapes (temp); + } + + free (temp1); + + goto return0; + } + + temp = (char *)NULL; + +unbound_variable: + if (unbound_vars_is_error) + { + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (temp1); + } + else + { + free (temp1); + goto return0; + } + + free (temp1); + last_command_exit_value = EXECUTION_FAILURE; + return ((unbound_vars_is_error && interactive_shell == 0) + ? &expand_wdesc_fatal + : &expand_wdesc_error); + } + + if (string[zindex]) + zindex++; + +return0: + *sindex = zindex; + + if (ret == 0) + { + ret = alloc_word_desc (); + ret->flags = tflag; /* XXX */ + ret->word = temp; + } + return ret; +} + +/* Make a word list which is the result of parameter and variable + expansion, command substitution, arithmetic substitution, and + quote removal of WORD. Return a pointer to a WORD_LIST which is + the result of the expansion. If WORD contains a null word, the + word list returned is also null. + + QUOTED contains flag values defined in shell.h. + + ISEXP is used to tell expand_word_internal that the word should be + treated as the result of an expansion. This has implications for + how IFS characters in the word are treated. + + CONTAINS_DOLLAR_AT and EXPANDED_SOMETHING are return values; when non-null + they point to an integer value which receives information about expansion. + CONTAINS_DOLLAR_AT gets non-zero if WORD contained "$@", else zero. + EXPANDED_SOMETHING get non-zero if WORD contained any parameter expansions, + else zero. + + This only does word splitting in the case of $@ expansion. In that + case, we split on ' '. */ + +/* Values for the local variable quoted_state. */ +#define UNQUOTED 0 +#define PARTIALLY_QUOTED 1 +#define WHOLLY_QUOTED 2 + +static WORD_LIST * +expand_word_internal (word, quoted, isexp, contains_dollar_at, expanded_something) + WORD_DESC *word; + int quoted, isexp; + int *contains_dollar_at; + int *expanded_something; +{ + WORD_LIST *list; + WORD_DESC *tword; + + /* The intermediate string that we build while expanding. */ + char *istring; + + /* The current size of the above object. */ + int istring_size; + + /* Index into ISTRING. */ + int istring_index; + + /* Temporary string storage. */ + char *temp, *temp1; + + /* The text of WORD. */ + register char *string; + + /* The size of STRING. */ + size_t string_size; + + /* The index into STRING. */ + int sindex; + + /* This gets 1 if we see a $@ while quoted. */ + int quoted_dollar_at; + + /* One of UNQUOTED, PARTIALLY_QUOTED, or WHOLLY_QUOTED, depending on + whether WORD contains no quoting characters, a partially quoted + string (e.g., "xx"ab), or is fully quoted (e.g., "xxab"). */ + int quoted_state; + + /* State flags */ + int had_quoted_null; + int has_dollar_at; + int tflag; + int pflags; /* flags passed to param_expand */ + + int assignoff; /* If assignment, offset of `=' */ + + register unsigned char c; /* Current character. */ + int t_index; /* For calls to string_extract_xxx. */ + + char twochars[2]; + + DECLARE_MBSTATE; + + istring = (char *)xmalloc (istring_size = DEFAULT_INITIAL_ARRAY_SIZE); + istring[istring_index = 0] = '\0'; + quoted_dollar_at = had_quoted_null = has_dollar_at = 0; + quoted_state = UNQUOTED; + + string = word->word; + if (string == 0) + goto finished_with_string; + /* Don't need the string length for the SADD... and COPY_ macros unless + multibyte characters are possible. */ + string_size = (MB_CUR_MAX > 1) ? strlen (string) : 1; + + if (contains_dollar_at) + *contains_dollar_at = 0; + + assignoff = -1; + + /* Begin the expansion. */ + + for (sindex = 0; ;) + { + c = string[sindex]; + + /* Case on toplevel character. */ + switch (c) + { + case '\0': + goto finished_with_string; + + case CTLESC: + sindex++; +#if HANDLE_MULTIBYTE + if (MB_CUR_MAX > 1 && string[sindex]) + { + SADD_MBQCHAR_BODY(temp, string, sindex, string_size); + } + else +#endif + { + temp = (char *)xmalloc (3); + temp[0] = CTLESC; + temp[1] = c = string[sindex]; + temp[2] = '\0'; + } + +dollar_add_string: + if (string[sindex]) + sindex++; + +add_string: + if (temp) + { + istring = sub_append_string (temp, istring, &istring_index, &istring_size); + temp = (char *)0; + } + + break; + +#if defined (PROCESS_SUBSTITUTION) + /* Process substitution. */ + case '<': + case '>': + { + if (string[++sindex] != LPAREN || (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (word->flags & (W_DQUOTE|W_NOPROCSUB)) || posixly_correct) + { + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + else + t_index = sindex + 1; /* skip past both '<' and LPAREN */ + + temp1 = extract_process_subst (string, (c == '<') ? "<(" : ">(", &t_index); /*))*/ + sindex = t_index; + + /* If the process substitution specification is `<()', we want to + open the pipe for writing in the child and produce output; if + it is `>()', we want to open the pipe for reading in the child + and consume input. */ + temp = temp1 ? process_substitute (temp1, (c == '>')) : (char *)0; + + FREE (temp1); + + goto dollar_add_string; + } +#endif /* PROCESS_SUBSTITUTION */ + + case '=': + /* Posix.2 section 3.6.1 says that tildes following `=' in words + which are not assignment statements are not expanded. If the + shell isn't in posix mode, though, we perform tilde expansion + on `likely candidate' unquoted assignment statements (flags + include W_ASSIGNMENT but not W_QUOTED). A likely candidate + contains an unquoted :~ or =~. Something to think about: we + now have a flag that says to perform tilde expansion on arguments + to `assignment builtins' like declare and export that look like + assignment statements. We now do tilde expansion on such words + even in POSIX mode. */ + if (word->flags & (W_ASSIGNRHS|W_NOTILDE)) + { + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + } + /* If we're not in posix mode or forcing assignment-statement tilde + expansion, note where the `=' appears in the word and prepare to + do tilde expansion following the first `='. */ + if ((word->flags & W_ASSIGNMENT) && + (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && + assignoff == -1 && sindex > 0) + assignoff = sindex; + if (sindex == assignoff && string[sindex+1] == '~') /* XXX */ + word->flags |= W_ITILDE; +#if 0 + else if ((word->flags & W_ASSIGNMENT) && + (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && + string[sindex+1] == '~') + word->flags |= W_ITILDE; +#endif + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + + case ':': + if (word->flags & W_NOTILDE) + { + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + } + + if ((word->flags & (W_ASSIGNMENT|W_ASSIGNRHS|W_TILDEEXP)) && + string[sindex+1] == '~') + word->flags |= W_ITILDE; + + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + + case '~': + /* If the word isn't supposed to be tilde expanded, or we're not + at the start of a word or after an unquoted : or = in an + assignment statement, we don't do tilde expansion. */ + if ((word->flags & (W_NOTILDE|W_DQUOTE)) || + (sindex > 0 && ((word->flags & W_ITILDE) == 0)) || + (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + { + word->flags &= ~W_ITILDE; + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0) + goto add_ifs_character; + else + goto add_character; + } + + if (word->flags & W_ASSIGNRHS) + tflag = 2; + else if (word->flags & (W_ASSIGNMENT|W_TILDEEXP)) + tflag = 1; + else + tflag = 0; + + temp = bash_tilde_find_word (string + sindex, tflag, &t_index); + + word->flags &= ~W_ITILDE; + + if (temp && *temp && t_index > 0) + { + temp1 = bash_tilde_expand (temp, tflag); + if (temp1 && *temp1 == '~' && STREQ (temp, temp1)) + { + FREE (temp); + FREE (temp1); + goto add_character; /* tilde expansion failed */ + } + free (temp); + temp = temp1; + sindex += t_index; + goto add_quoted_string; /* XXX was add_string */ + } + else + { + FREE (temp); + goto add_character; + } + + case '$': + if (expanded_something) + *expanded_something = 1; + + has_dollar_at = 0; + pflags = (word->flags & W_NOCOMSUB) ? PF_NOCOMSUB : 0; + if (word->flags & W_NOSPLIT2) + pflags |= PF_NOSPLIT2; + tword = param_expand (string, &sindex, quoted, expanded_something, + &has_dollar_at, "ed_dollar_at, + &had_quoted_null, pflags); + + if (tword == &expand_wdesc_error || tword == &expand_wdesc_fatal) + { + free (string); + free (istring); + return ((tword == &expand_wdesc_error) ? &expand_word_error + : &expand_word_fatal); + } + if (contains_dollar_at && has_dollar_at) + *contains_dollar_at = 1; + + if (tword && (tword->flags & W_HASQUOTEDNULL)) + had_quoted_null = 1; + + temp = tword->word; + dispose_word_desc (tword); + + goto add_string; + break; + + case '`': /* Backquoted command substitution. */ + { + t_index = sindex++; + + temp = string_extract (string, &sindex, "`", SX_REQMATCH); + /* The test of sindex against t_index is to allow bare instances of + ` to pass through, for backwards compatibility. */ + if (temp == &extract_string_error || temp == &extract_string_fatal) + { + if (sindex - 1 == t_index) + { + sindex = t_index; + goto add_character; + } + report_error (_("bad substitution: no closing \"`\" in %s") , string+t_index); + free (string); + free (istring); + return ((temp == &extract_string_error) ? &expand_word_error + : &expand_word_fatal); + } + + if (expanded_something) + *expanded_something = 1; + + if (word->flags & W_NOCOMSUB) + /* sindex + 1 because string[sindex] == '`' */ + temp1 = substring (string, t_index, sindex + 1); + else + { + de_backslash (temp); + tword = command_substitute (temp, quoted); + temp1 = tword ? tword->word : (char *)NULL; + if (tword) + dispose_word_desc (tword); + } + FREE (temp); + temp = temp1; + goto dollar_add_string; + } + + case '\\': + if (string[sindex + 1] == '\n') + { + sindex += 2; + continue; + } + + c = string[++sindex]; + + if (quoted & Q_HERE_DOCUMENT) + tflag = CBSHDOC; + else if (quoted & Q_DOUBLE_QUOTES) + tflag = CBSDQUOTE; + else + tflag = 0; + + /* From Posix discussion on austin-group list: Backslash escaping + a } in ${...} is removed. Issue 0000221 */ + if ((quoted & Q_DOLBRACE) && c == RBRACE) + { + SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size); + } + else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0)) + { + SCOPY_CHAR_I (twochars, '\\', c, string, sindex, string_size); + } + else if (c == 0) + { + c = CTLNUL; + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + else + { + SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size); + } + + sindex++; +add_twochars: + /* BEFORE jumping here, we need to increment sindex if appropriate */ + RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, + DEFAULT_ARRAY_SIZE); + istring[istring_index++] = twochars[0]; + istring[istring_index++] = twochars[1]; + istring[istring_index] = '\0'; + + break; + + case '"': +#if 0 + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (word->flags & W_DQUOTE)) +#else + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) +#endif + goto add_character; + + t_index = ++sindex; + temp = string_extract_double_quoted (string, &sindex, 0); + + /* If the quotes surrounded the entire string, then the + whole word was quoted. */ + quoted_state = (t_index == 1 && string[sindex] == '\0') + ? WHOLLY_QUOTED + : PARTIALLY_QUOTED; + + if (temp && *temp) + { + tword = alloc_word_desc (); + tword->word = temp; + + temp = (char *)NULL; + + has_dollar_at = 0; + /* Need to get W_HASQUOTEDNULL flag through this function. */ + list = expand_word_internal (tword, Q_DOUBLE_QUOTES, 0, &has_dollar_at, (int *)NULL); + + if (list == &expand_word_error || list == &expand_word_fatal) + { + free (istring); + free (string); + /* expand_word_internal has already freed temp_word->word + for us because of the way it prints error messages. */ + tword->word = (char *)NULL; + dispose_word (tword); + return list; + } + + dispose_word (tword); + + /* "$@" (a double-quoted dollar-at) expands into nothing, + not even a NULL word, when there are no positional + parameters. */ + if (list == 0 && has_dollar_at) + { + quoted_dollar_at++; + break; + } + + /* If we get "$@", we know we have expanded something, so we + need to remember it for the final split on $IFS. This is + a special case; it's the only case where a quoted string + can expand into more than one word. It's going to come back + from the above call to expand_word_internal as a list with + a single word, in which all characters are quoted and + separated by blanks. What we want to do is to turn it back + into a list for the next piece of code. */ + if (list) + dequote_list (list); + + if (list && list->word && (list->word->flags & W_HASQUOTEDNULL)) + had_quoted_null = 1; + + if (has_dollar_at) + { + quoted_dollar_at++; + if (contains_dollar_at) + *contains_dollar_at = 1; + if (expanded_something) + *expanded_something = 1; + } + } + else + { + /* What we have is "". This is a minor optimization. */ + FREE (temp); + list = (WORD_LIST *)NULL; + } + + /* The code above *might* return a list (consider the case of "$@", + where it returns "$1", "$2", etc.). We can't throw away the + rest of the list, and we have to make sure each word gets added + as quoted. We test on tresult->next: if it is non-NULL, we + quote the whole list, save it to a string with string_list, and + add that string. We don't need to quote the results of this + (and it would be wrong, since that would quote the separators + as well), so we go directly to add_string. */ + if (list) + { + if (list->next) + { +#if 0 + if (quoted_dollar_at && word->flags & W_NOSPLIT2) + temp = string_list_internal (quote_list (list), " "); + else +#endif + /* Testing quoted_dollar_at makes sure that "$@" is + split correctly when $IFS does not contain a space. */ + temp = quoted_dollar_at + ? string_list_dollar_at (list, Q_DOUBLE_QUOTES) + : string_list (quote_list (list)); + dispose_words (list); + goto add_string; + } + else + { + temp = savestring (list->word->word); + tflag = list->word->flags; + dispose_words (list); + + /* If the string is not a quoted null string, we want + to remove any embedded unquoted CTLNUL characters. + We do not want to turn quoted null strings back into + the empty string, though. We do this because we + want to remove any quoted nulls from expansions that + contain other characters. For example, if we have + x"$*"y or "x$*y" and there are no positional parameters, + the $* should expand into nothing. */ + /* We use the W_HASQUOTEDNULL flag to differentiate the + cases: a quoted null character as above and when + CTLNUL is contained in the (non-null) expansion + of some variable. We use the had_quoted_null flag to + pass the value through this function to its caller. */ + if ((tflag & W_HASQUOTEDNULL) && QUOTED_NULL (temp) == 0) + remove_quoted_nulls (temp); /* XXX */ + } + } + else + temp = (char *)NULL; + + /* We do not want to add quoted nulls to strings that are only + partially quoted; we can throw them away. */ + if (temp == 0 && quoted_state == PARTIALLY_QUOTED) + continue; + + add_quoted_string: + + if (temp) + { + temp1 = temp; + temp = quote_string (temp); + free (temp1); + goto add_string; + } + else + { + /* Add NULL arg. */ + c = CTLNUL; + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + + /* break; */ + + case '\'': +#if 0 + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (word->flags & W_DQUOTE)) +#else + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) +#endif + goto add_character; + + t_index = ++sindex; + temp = string_extract_single_quoted (string, &sindex); + + /* If the entire STRING was surrounded by single quotes, + then the string is wholly quoted. */ + quoted_state = (t_index == 1 && string[sindex] == '\0') + ? WHOLLY_QUOTED + : PARTIALLY_QUOTED; + + /* If all we had was '', it is a null expansion. */ + if (*temp == '\0') + { + free (temp); + temp = (char *)NULL; + } + else + remove_quoted_escapes (temp); /* ??? */ + + /* We do not want to add quoted nulls to strings that are only + partially quoted; such nulls are discarded. */ + if (temp == 0 && (quoted_state == PARTIALLY_QUOTED)) + continue; + + /* If we have a quoted null expansion, add a quoted NULL to istring. */ + if (temp == 0) + { + c = CTLNUL; + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + else + goto add_quoted_string; + + /* break; */ + + default: + /* This is the fix for " $@ " */ + add_ifs_character: + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (isexp == 0 && isifs (c))) + { + if (string[sindex]) /* from old goto dollar_add_string */ + sindex++; + if (c == 0) + { + c = CTLNUL; + goto add_character; + } + else + { +#if HANDLE_MULTIBYTE + if (MB_CUR_MAX > 1) + sindex--; + + if (MB_CUR_MAX > 1) + { + SADD_MBQCHAR_BODY(temp, string, sindex, string_size); + } + else +#endif + { + twochars[0] = CTLESC; + twochars[1] = c; + goto add_twochars; + } + } + } + + SADD_MBCHAR (temp, string, sindex, string_size); + + add_character: + RESIZE_MALLOCED_BUFFER (istring, istring_index, 1, istring_size, + DEFAULT_ARRAY_SIZE); + istring[istring_index++] = c; + istring[istring_index] = '\0'; + + /* Next character. */ + sindex++; + } + } + +finished_with_string: + /* OK, we're ready to return. If we have a quoted string, and + quoted_dollar_at is not set, we do no splitting at all; otherwise + we split on ' '. The routines that call this will handle what to + do if nothing has been expanded. */ + + /* Partially and wholly quoted strings which expand to the empty + string are retained as an empty arguments. Unquoted strings + which expand to the empty string are discarded. The single + exception is the case of expanding "$@" when there are no + positional parameters. In that case, we discard the expansion. */ + + /* Because of how the code that handles "" and '' in partially + quoted strings works, we need to make ISTRING into a QUOTED_NULL + if we saw quoting characters, but the expansion was empty. + "" and '' are tossed away before we get to this point when + processing partially quoted strings. This makes "" and $xxx"" + equivalent when xxx is unset. We also look to see whether we + saw a quoted null from a ${} expansion and add one back if we + need to. */ + + /* If we expand to nothing and there were no single or double quotes + in the word, we throw it away. Otherwise, we return a NULL word. + The single exception is for $@ surrounded by double quotes when + there are no positional parameters. In that case, we also throw + the word away. */ + + if (*istring == '\0') + { + if (quoted_dollar_at == 0 && (had_quoted_null || quoted_state == PARTIALLY_QUOTED)) + { + istring[0] = CTLNUL; + istring[1] = '\0'; + tword = make_bare_word (istring); + tword->flags |= W_HASQUOTEDNULL; /* XXX */ + list = make_word_list (tword, (WORD_LIST *)NULL); + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + tword->flags |= W_QUOTED; + } + /* According to sh, ksh, and Posix.2, if a word expands into nothing + and a double-quoted "$@" appears anywhere in it, then the entire + word is removed. */ + else if (quoted_state == UNQUOTED || quoted_dollar_at) + list = (WORD_LIST *)NULL; +#if 0 + else + { + tword = make_bare_word (istring); + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + tword->flags |= W_QUOTED; + list = make_word_list (tword, (WORD_LIST *)NULL); + } +#else + else + list = (WORD_LIST *)NULL; +#endif + } + else if (word->flags & W_NOSPLIT) + { + tword = make_bare_word (istring); + if (word->flags & W_ASSIGNMENT) + tword->flags |= W_ASSIGNMENT; /* XXX */ + if (word->flags & W_COMPASSIGN) + tword->flags |= W_COMPASSIGN; /* XXX */ + if (word->flags & W_NOGLOB) + tword->flags |= W_NOGLOB; /* XXX */ + if (word->flags & W_NOEXPAND) + tword->flags |= W_NOEXPAND; /* XXX */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + tword->flags |= W_QUOTED; + if (had_quoted_null) + tword->flags |= W_HASQUOTEDNULL; + list = make_word_list (tword, (WORD_LIST *)NULL); + } + else + { + char *ifs_chars; + + ifs_chars = (quoted_dollar_at || has_dollar_at) ? ifs_value : (char *)NULL; + + /* If we have $@, we need to split the results no matter what. If + IFS is unset or NULL, string_list_dollar_at has separated the + positional parameters with a space, so we split on space (we have + set ifs_chars to " \t\n" above if ifs is unset). If IFS is set, + string_list_dollar_at has separated the positional parameters + with the first character of $IFS, so we split on $IFS. */ + if (has_dollar_at && ifs_chars) + list = list_string (istring, *ifs_chars ? ifs_chars : " ", 1); + else + { + tword = make_bare_word (istring); + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (quoted_state == WHOLLY_QUOTED)) + tword->flags |= W_QUOTED; + if (word->flags & W_ASSIGNMENT) + tword->flags |= W_ASSIGNMENT; + if (word->flags & W_COMPASSIGN) + tword->flags |= W_COMPASSIGN; + if (word->flags & W_NOGLOB) + tword->flags |= W_NOGLOB; + if (word->flags & W_NOEXPAND) + tword->flags |= W_NOEXPAND; + if (had_quoted_null) + tword->flags |= W_HASQUOTEDNULL; /* XXX */ + list = make_word_list (tword, (WORD_LIST *)NULL); + } + } + + free (istring); + return (list); +} + +/* **************************************************************** */ +/* */ +/* Functions for Quote Removal */ +/* */ +/* **************************************************************** */ + +/* Perform quote removal on STRING. If QUOTED > 0, assume we are obeying the + backslash quoting rules for within double quotes or a here document. */ +char * +string_quote_removal (string, quoted) + char *string; + int quoted; +{ + size_t slen; + char *r, *result_string, *temp, *send; + int sindex, tindex, dquote; + unsigned char c; + DECLARE_MBSTATE; + + /* The result can be no longer than the original string. */ + slen = strlen (string); + send = string + slen; + + r = result_string = (char *)xmalloc (slen + 1); + + for (dquote = sindex = 0; c = string[sindex];) + { + switch (c) + { + case '\\': + c = string[++sindex]; + if (c == 0) + { + *r++ = '\\'; + break; + } + if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && (sh_syntaxtab[c] & CBSDQUOTE) == 0) + *r++ = '\\'; + /* FALLTHROUGH */ + + default: + SCOPY_CHAR_M (r, string, send, sindex); + break; + + case '\'': + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) + { + *r++ = c; + sindex++; + break; + } + tindex = sindex + 1; + temp = string_extract_single_quoted (string, &tindex); + if (temp) + { + strcpy (r, temp); + r += strlen (r); + free (temp); + } + sindex = tindex; + break; + + case '"': + dquote = 1 - dquote; + sindex++; + break; + } + } + *r = '\0'; + return (result_string); +} + +#if 0 +/* UNUSED */ +/* Perform quote removal on word WORD. This allocates and returns a new + WORD_DESC *. */ +WORD_DESC * +word_quote_removal (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_DESC *w; + char *t; + + t = string_quote_removal (word->word, quoted); + w = alloc_word_desc (); + w->word = t ? t : savestring (""); + return (w); +} + +/* Perform quote removal on all words in LIST. If QUOTED is non-zero, + the members of the list are treated as if they are surrounded by + double quotes. Return a new list, or NULL if LIST is NULL. */ +WORD_LIST * +word_list_quote_removal (list, quoted) + WORD_LIST *list; + int quoted; +{ + WORD_LIST *result, *t, *tresult, *e; + + for (t = list, result = (WORD_LIST *)NULL; t; t = t->next) + { + tresult = make_word_list (word_quote_removal (t->word, quoted), (WORD_LIST *)NULL); +#if 0 + result = (WORD_LIST *) list_append (result, tresult); +#else + if (result == 0) + result = e = tresult; + else + { + e->next = tresult; + while (e->next) + e = e->next; + } +#endif + } + return (result); +} +#endif + +/******************************************* + * * + * Functions to perform word splitting * + * * + *******************************************/ + +void +setifs (v) + SHELL_VAR *v; +{ + char *t; + unsigned char uc; + + ifs_var = v; + ifs_value = (v && value_cell (v)) ? value_cell (v) : " \t\n"; + + /* Should really merge ifs_cmap with sh_syntaxtab. XXX - doesn't yet + handle multibyte chars in IFS */ + memset (ifs_cmap, '\0', sizeof (ifs_cmap)); + for (t = ifs_value ; t && *t; t++) + { + uc = *t; + ifs_cmap[uc] = 1; + } + +#if defined (HANDLE_MULTIBYTE) + if (ifs_value == 0) + { + ifs_firstc[0] = '\0'; + ifs_firstc_len = 1; + } + else + { + size_t ifs_len; + ifs_len = strnlen (ifs_value, MB_CUR_MAX); + ifs_firstc_len = MBLEN (ifs_value, ifs_len); + if (ifs_firstc_len == 1 || ifs_firstc_len == 0 || MB_INVALIDCH (ifs_firstc_len)) + { + ifs_firstc[0] = ifs_value[0]; + ifs_firstc[1] = '\0'; + ifs_firstc_len = 1; + } + else + memcpy (ifs_firstc, ifs_value, ifs_firstc_len); + } +#else + ifs_firstc = ifs_value ? *ifs_value : 0; +#endif +} + +char * +getifs () +{ + return ifs_value; +} + +/* This splits a single word into a WORD LIST on $IFS, but only if the word + is not quoted. list_string () performs quote removal for us, even if we + don't do any splitting. */ +WORD_LIST * +word_split (w, ifs_chars) + WORD_DESC *w; + char *ifs_chars; +{ + WORD_LIST *result; + + if (w) + { + char *xifs; + + xifs = ((w->flags & W_QUOTED) || ifs_chars == 0) ? "" : ifs_chars; + result = list_string (w->word, xifs, w->flags & W_QUOTED); + } + else + result = (WORD_LIST *)NULL; + + return (result); +} + +/* Perform word splitting on LIST and return the RESULT. It is possible + to return (WORD_LIST *)NULL. */ +static WORD_LIST * +word_list_split (list) + WORD_LIST *list; +{ + WORD_LIST *result, *t, *tresult, *e; + + for (t = list, result = (WORD_LIST *)NULL; t; t = t->next) + { + tresult = word_split (t->word, ifs_value); + if (result == 0) + result = e = tresult; + else + { + e->next = tresult; + while (e->next) + e = e->next; + } + } + return (result); +} + +/************************************************** + * * + * Functions to expand an entire WORD_LIST * + * * + **************************************************/ + +/* Do any word-expansion-specific cleanup and jump to top_level */ +static void +exp_jump_to_top_level (v) + int v; +{ + set_pipestatus_from_exit (last_command_exit_value); + + /* Cleanup code goes here. */ + expand_no_split_dollar_star = 0; /* XXX */ + expanding_redir = 0; + assigning_in_environment = 0; + + if (parse_and_execute_level == 0) + top_level_cleanup (); /* from sig.c */ + + jump_to_top_level (v); +} + +/* Put NLIST (which is a WORD_LIST * of only one element) at the front of + ELIST, and set ELIST to the new list. */ +#define PREPEND_LIST(nlist, elist) \ + do { nlist->next = elist; elist = nlist; } while (0) + +/* Separate out any initial variable assignments from TLIST. If set -k has + been executed, remove all assignment statements from TLIST. Initial + variable assignments and other environment assignments are placed + on SUBST_ASSIGN_VARLIST. */ +static WORD_LIST * +separate_out_assignments (tlist) + WORD_LIST *tlist; +{ + register WORD_LIST *vp, *lp; + + if (tlist == 0) + return ((WORD_LIST *)NULL); + + if (subst_assign_varlist) + dispose_words (subst_assign_varlist); /* Clean up after previous error */ + + subst_assign_varlist = (WORD_LIST *)NULL; + vp = lp = tlist; + + /* Separate out variable assignments at the start of the command. + Loop invariant: vp->next == lp + Loop postcondition: + lp = list of words left after assignment statements skipped + tlist = original list of words + */ + while (lp && (lp->word->flags & W_ASSIGNMENT)) + { + vp = lp; + lp = lp->next; + } + + /* If lp != tlist, we have some initial assignment statements. + We make SUBST_ASSIGN_VARLIST point to the list of assignment + words and TLIST point to the remaining words. */ + if (lp != tlist) + { + subst_assign_varlist = tlist; + /* ASSERT(vp->next == lp); */ + vp->next = (WORD_LIST *)NULL; /* terminate variable list */ + tlist = lp; /* remainder of word list */ + } + + /* vp == end of variable list */ + /* tlist == remainder of original word list without variable assignments */ + if (!tlist) + /* All the words in tlist were assignment statements */ + return ((WORD_LIST *)NULL); + + /* ASSERT(tlist != NULL); */ + /* ASSERT((tlist->word->flags & W_ASSIGNMENT) == 0); */ + + /* If the -k option is in effect, we need to go through the remaining + words, separate out the assignment words, and place them on + SUBST_ASSIGN_VARLIST. */ + if (place_keywords_in_env) + { + WORD_LIST *tp; /* tp == running pointer into tlist */ + + tp = tlist; + lp = tlist->next; + + /* Loop Invariant: tp->next == lp */ + /* Loop postcondition: tlist == word list without assignment statements */ + while (lp) + { + if (lp->word->flags & W_ASSIGNMENT) + { + /* Found an assignment statement, add this word to end of + subst_assign_varlist (vp). */ + if (!subst_assign_varlist) + subst_assign_varlist = vp = lp; + else + { + vp->next = lp; + vp = lp; + } + + /* Remove the word pointed to by LP from TLIST. */ + tp->next = lp->next; + /* ASSERT(vp == lp); */ + lp->next = (WORD_LIST *)NULL; + lp = tp->next; + } + else + { + tp = lp; + lp = lp->next; + } + } + } + return (tlist); +} + +#define WEXP_VARASSIGN 0x001 +#define WEXP_BRACEEXP 0x002 +#define WEXP_TILDEEXP 0x004 +#define WEXP_PARAMEXP 0x008 +#define WEXP_PATHEXP 0x010 + +/* All of the expansions, including variable assignments at the start of + the list. */ +#define WEXP_ALL (WEXP_VARASSIGN|WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP) + +/* All of the expansions except variable assignments at the start of + the list. */ +#define WEXP_NOVARS (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP) + +/* All of the `shell expansions': brace expansion, tilde expansion, parameter + expansion, command substitution, arithmetic expansion, word splitting, and + quote removal. */ +#define WEXP_SHELLEXP (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP) + +/* Take the list of words in LIST and do the various substitutions. Return + a new list of words which is the expanded list, and without things like + variable assignments. */ + +WORD_LIST * +expand_words (list) + WORD_LIST *list; +{ + return (expand_word_list_internal (list, WEXP_ALL)); +} + +/* Same as expand_words (), but doesn't hack variable or environment + variables. */ +WORD_LIST * +expand_words_no_vars (list) + WORD_LIST *list; +{ + return (expand_word_list_internal (list, WEXP_NOVARS)); +} + +WORD_LIST * +expand_words_shellexp (list) + WORD_LIST *list; +{ + return (expand_word_list_internal (list, WEXP_SHELLEXP)); +} + +static WORD_LIST * +glob_expand_word_list (tlist, eflags) + WORD_LIST *tlist; + int eflags; +{ + char **glob_array, *temp_string; + register int glob_index; + WORD_LIST *glob_list, *output_list, *disposables, *next; + WORD_DESC *tword; + + output_list = disposables = (WORD_LIST *)NULL; + glob_array = (char **)NULL; + while (tlist) + { + /* For each word, either globbing is attempted or the word is + added to orig_list. If globbing succeeds, the results are + added to orig_list and the word (tlist) is added to the list + of disposable words. If globbing fails and failed glob + expansions are left unchanged (the shell default), the + original word is added to orig_list. If globbing fails and + failed glob expansions are removed, the original word is + added to the list of disposable words. orig_list ends up + in reverse order and requires a call to REVERSE_LIST to + be set right. After all words are examined, the disposable + words are freed. */ + next = tlist->next; + + /* If the word isn't an assignment and contains an unquoted + pattern matching character, then glob it. */ + if ((tlist->word->flags & W_NOGLOB) == 0 && + unquoted_glob_pattern_p (tlist->word->word)) + { + glob_array = shell_glob_filename (tlist->word->word); + + /* Handle error cases. + I don't think we should report errors like "No such file + or directory". However, I would like to report errors + like "Read failed". */ + + if (glob_array == 0 || GLOB_FAILED (glob_array)) + { + glob_array = (char **)xmalloc (sizeof (char *)); + glob_array[0] = (char *)NULL; + } + + /* Dequote the current word in case we have to use it. */ + if (glob_array[0] == NULL) + { + temp_string = dequote_string (tlist->word->word); + free (tlist->word->word); + tlist->word->word = temp_string; + } + + /* Make the array into a word list. */ + glob_list = (WORD_LIST *)NULL; + for (glob_index = 0; glob_array[glob_index]; glob_index++) + { + tword = make_bare_word (glob_array[glob_index]); + tword->flags |= W_GLOBEXP; /* XXX */ + glob_list = make_word_list (tword, glob_list); + } + + if (glob_list) + { + output_list = (WORD_LIST *)list_append (glob_list, output_list); + PREPEND_LIST (tlist, disposables); + } + else if (fail_glob_expansion != 0) + { + report_error (_("no match: %s"), tlist->word->word); + exp_jump_to_top_level (DISCARD); + } + else if (allow_null_glob_expansion == 0) + { + /* Failed glob expressions are left unchanged. */ + PREPEND_LIST (tlist, output_list); + } + else + { + /* Failed glob expressions are removed. */ + PREPEND_LIST (tlist, disposables); + } + } + else + { + /* Dequote the string. */ + temp_string = dequote_string (tlist->word->word); + free (tlist->word->word); + tlist->word->word = temp_string; + PREPEND_LIST (tlist, output_list); + } + + strvec_dispose (glob_array); + glob_array = (char **)NULL; + + tlist = next; + } + + if (disposables) + dispose_words (disposables); + + if (output_list) + output_list = REVERSE_LIST (output_list, WORD_LIST *); + + return (output_list); +} + +#if defined (BRACE_EXPANSION) +static WORD_LIST * +brace_expand_word_list (tlist, eflags) + WORD_LIST *tlist; + int eflags; +{ + register char **expansions; + char *temp_string; + WORD_LIST *disposables, *output_list, *next; + WORD_DESC *w; + int eindex; + + for (disposables = output_list = (WORD_LIST *)NULL; tlist; tlist = next) + { + next = tlist->next; + + if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG)) + { +/*itrace("brace_expand_word_list: %s: W_COMPASSIGN|W_ASSIGNARG", tlist->word->word);*/ + PREPEND_LIST (tlist, output_list); + continue; + } + + /* Only do brace expansion if the word has a brace character. If + not, just add the word list element to BRACES and continue. In + the common case, at least when running shell scripts, this will + degenerate to a bunch of calls to `mbschr', and then what is + basically a reversal of TLIST into BRACES, which is corrected + by a call to REVERSE_LIST () on BRACES when the end of TLIST + is reached. */ + if (mbschr (tlist->word->word, LBRACE)) + { + expansions = brace_expand (tlist->word->word); + + for (eindex = 0; temp_string = expansions[eindex]; eindex++) + { + w = make_word (temp_string); + /* If brace expansion didn't change the word, preserve + the flags. We may want to preserve the flags + unconditionally someday -- XXX */ + if (STREQ (temp_string, tlist->word->word)) + w->flags = tlist->word->flags; + output_list = make_word_list (w, output_list); + free (expansions[eindex]); + } + free (expansions); + + /* Add TLIST to the list of words to be freed after brace + expansion has been performed. */ + PREPEND_LIST (tlist, disposables); + } + else + PREPEND_LIST (tlist, output_list); + } + + if (disposables) + dispose_words (disposables); + + if (output_list) + output_list = REVERSE_LIST (output_list, WORD_LIST *); + + return (output_list); +} +#endif + +#if defined (ARRAY_VARS) +/* Take WORD, a compound associative array assignment, and internally run + 'declare -A w', where W is the variable name portion of WORD. */ +static int +make_internal_declare (word, option) + char *word; + char *option; +{ + int t; + WORD_LIST *wl; + WORD_DESC *w; + + w = make_word (word); + + t = assignment (w->word, 0); + w->word[t] = '\0'; + + wl = make_word_list (w, (WORD_LIST *)NULL); + wl = make_word_list (make_word (option), wl); + + return (declare_builtin (wl)); +} +#endif + +static WORD_LIST * +shell_expand_word_list (tlist, eflags) + WORD_LIST *tlist; + int eflags; +{ + WORD_LIST *expanded, *orig_list, *new_list, *next, *temp_list; + int expanded_something, has_dollar_at; + char *temp_string; + + /* We do tilde expansion all the time. This is what 1003.2 says. */ + new_list = (WORD_LIST *)NULL; + for (orig_list = tlist; tlist; tlist = next) + { + temp_string = tlist->word->word; + + next = tlist->next; + +#if defined (ARRAY_VARS) + /* If this is a compound array assignment to a builtin that accepts + such assignments (e.g., `declare'), take the assignment and perform + it separately, handling the semantics of declarations inside shell + functions. This avoids the double-evaluation of such arguments, + because `declare' does some evaluation of compound assignments on + its own. */ + if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG)) + { + int t; + + if (tlist->word->flags & W_ASSIGNASSOC) + make_internal_declare (tlist->word->word, "-A"); + + t = do_word_assignment (tlist->word); + if (t == 0) + { + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level (DISCARD); + } + + /* Now transform the word as ksh93 appears to do and go on */ + t = assignment (tlist->word->word, 0); + tlist->word->word[t] = '\0'; + tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC); + } +#endif + + expanded_something = 0; + expanded = expand_word_internal + (tlist->word, 0, 0, &has_dollar_at, &expanded_something); + + if (expanded == &expand_word_error || expanded == &expand_word_fatal) + { + /* By convention, each time this error is returned, + tlist->word->word has already been freed. */ + tlist->word->word = (char *)NULL; + + /* Dispose our copy of the original list. */ + dispose_words (orig_list); + /* Dispose the new list we're building. */ + dispose_words (new_list); + + last_command_exit_value = EXECUTION_FAILURE; + if (expanded == &expand_word_error) + exp_jump_to_top_level (DISCARD); + else + exp_jump_to_top_level (FORCE_EOF); + } + + /* Don't split words marked W_NOSPLIT. */ + if (expanded_something && (tlist->word->flags & W_NOSPLIT) == 0) + { + temp_list = word_list_split (expanded); + dispose_words (expanded); + } + else + { + /* If no parameter expansion, command substitution, process + substitution, or arithmetic substitution took place, then + do not do word splitting. We still have to remove quoted + null characters from the result. */ + word_list_remove_quoted_nulls (expanded); + temp_list = expanded; + } + + expanded = REVERSE_LIST (temp_list, WORD_LIST *); + new_list = (WORD_LIST *)list_append (expanded, new_list); + } + + if (orig_list) + dispose_words (orig_list); + + if (new_list) + new_list = REVERSE_LIST (new_list, WORD_LIST *); + + return (new_list); +} + +/* The workhorse for expand_words () and expand_words_no_vars (). + First arg is LIST, a WORD_LIST of words. + Second arg EFLAGS is a flags word controlling which expansions are + performed. + + This does all of the substitutions: brace expansion, tilde expansion, + parameter expansion, command substitution, arithmetic expansion, + process substitution, word splitting, and pathname expansion, according + to the bits set in EFLAGS. Words with the W_QUOTED or W_NOSPLIT bits + set, or for which no expansion is done, do not undergo word splitting. + Words with the W_NOGLOB bit set do not undergo pathname expansion. */ +static WORD_LIST * +expand_word_list_internal (list, eflags) + WORD_LIST *list; + int eflags; +{ + WORD_LIST *new_list, *temp_list; + int tint; + + if (list == 0) + return ((WORD_LIST *)NULL); + + garglist = new_list = copy_word_list (list); + if (eflags & WEXP_VARASSIGN) + { + garglist = new_list = separate_out_assignments (new_list); + if (new_list == 0) + { + if (subst_assign_varlist) + { + /* All the words were variable assignments, so they are placed + into the shell's environment. */ + for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next) + { + this_command_name = (char *)NULL; /* no arithmetic errors */ + tint = do_word_assignment (temp_list->word); + /* Variable assignment errors in non-interactive shells + running in Posix.2 mode cause the shell to exit. */ + if (tint == 0) + { + last_command_exit_value = EXECUTION_FAILURE; + if (interactive_shell == 0 && posixly_correct) + exp_jump_to_top_level (FORCE_EOF); + else + exp_jump_to_top_level (DISCARD); + } + } + dispose_words (subst_assign_varlist); + subst_assign_varlist = (WORD_LIST *)NULL; + } + return ((WORD_LIST *)NULL); + } + } + + /* Begin expanding the words that remain. The expansions take place on + things that aren't really variable assignments. */ + +#if defined (BRACE_EXPANSION) + /* Do brace expansion on this word if there are any brace characters + in the string. */ + if ((eflags & WEXP_BRACEEXP) && brace_expansion && new_list) + new_list = brace_expand_word_list (new_list, eflags); +#endif /* BRACE_EXPANSION */ + + /* Perform the `normal' shell expansions: tilde expansion, parameter and + variable substitution, command substitution, arithmetic expansion, + and word splitting. */ + new_list = shell_expand_word_list (new_list, eflags); + + /* Okay, we're almost done. Now let's just do some filename + globbing. */ + if (new_list) + { + if ((eflags & WEXP_PATHEXP) && disallow_filename_globbing == 0) + /* Glob expand the word list unless globbing has been disabled. */ + new_list = glob_expand_word_list (new_list, eflags); + else + /* Dequote the words, because we're not performing globbing. */ + new_list = dequote_list (new_list); + } + + if ((eflags & WEXP_VARASSIGN) && subst_assign_varlist) + { + sh_wassign_func_t *assign_func; + + /* If the remainder of the words expand to nothing, Posix.2 requires + that the variable and environment assignments affect the shell's + environment. */ + assign_func = new_list ? assign_in_env : do_word_assignment; + tempenv_assign_error = 0; + + for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next) + { + this_command_name = (char *)NULL; + assigning_in_environment = (assign_func == assign_in_env); + tint = (*assign_func) (temp_list->word); + assigning_in_environment = 0; + /* Variable assignment errors in non-interactive shells running + in Posix.2 mode cause the shell to exit. */ + if (tint == 0) + { + if (assign_func == do_word_assignment) + { + last_command_exit_value = EXECUTION_FAILURE; + if (interactive_shell == 0 && posixly_correct) + exp_jump_to_top_level (FORCE_EOF); + else + exp_jump_to_top_level (DISCARD); + } + else + tempenv_assign_error++; + } + } + + dispose_words (subst_assign_varlist); + subst_assign_varlist = (WORD_LIST *)NULL; + } + +#if 0 + tint = list_length (new_list) + 1; + RESIZE_MALLOCED_BUFFER (glob_argv_flags, 0, tint, glob_argv_flags_size, 16); + for (tint = 0, temp_list = new_list; temp_list; temp_list = temp_list->next) + glob_argv_flags[tint++] = (temp_list->word->flags & W_GLOBEXP) ? '1' : '0'; + glob_argv_flags[tint] = '\0'; +#endif + + return (new_list); +} diff --git a/subst.c.save1 b/subst.c.save1 new file mode 100644 index 00000000..d2c0bf68 --- /dev/null +++ b/subst.c.save1 @@ -0,0 +1,9352 @@ +/* subst.c -- The part of the shell that does parameter, command, arithmetic, + and globbing substitutions. */ + +/* ``Have a little faith, there's magic in the night. You ain't a + beauty, but, hey, you're alright.'' */ + +/* Copyright (C) 1987-2010 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" +#include <stdio.h> +#include "chartypes.h" +#if defined (HAVE_PWD_H) +# include <pwd.h> +#endif +#include <signal.h> +#include <errno.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" +#include "posixstat.h" +#include "bashintl.h" + +#include "shell.h" +#include "parser.h" +#include "flags.h" +#include "jobs.h" +#include "execute_cmd.h" +#include "filecntl.h" +#include "trap.h" +#include "pathexp.h" +#include "mailcheck.h" + +#include "shmbutil.h" + +#include "builtins/getopt.h" +#include "builtins/common.h" + +#include "builtins/builtext.h" + +#include <tilde/tilde.h> +#include <glob/strmatch.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* The size that strings change by. */ +#define DEFAULT_INITIAL_ARRAY_SIZE 112 +#define DEFAULT_ARRAY_SIZE 128 + +/* Variable types. */ +#define VT_VARIABLE 0 +#define VT_POSPARMS 1 +#define VT_ARRAYVAR 2 +#define VT_ARRAYMEMBER 3 +#define VT_ASSOCVAR 4 + +#define VT_STARSUB 128 /* $* or ${array[*]} -- used to split */ + +/* Flags for quoted_strchr */ +#define ST_BACKSL 0x01 +#define ST_CTLESC 0x02 +#define ST_SQUOTE 0x04 /* unused yet */ +#define ST_DQUOTE 0x08 /* unused yet */ + +/* Flags for the `pflags' argument to param_expand() */ +#define PF_NOCOMSUB 0x01 /* Do not perform command substitution */ +#define PF_IGNUNBOUND 0x02 /* ignore unbound vars even if -u set */ +#define PF_NOSPLIT2 0x04 /* same as W_NOSPLIT2 */ + +/* These defs make it easier to use the editor. */ +#define LBRACE '{' +#define RBRACE '}' +#define LPAREN '(' +#define RPAREN ')' + +#if defined (HANDLE_MULTIBYTE) +#define WLPAREN L'(' +#define WRPAREN L')' +#endif + +/* Evaluates to 1 if C is one of the shell's special parameters whose length + can be taken, but is also one of the special expansion characters. */ +#define VALID_SPECIAL_LENGTH_PARAM(c) \ + ((c) == '-' || (c) == '?' || (c) == '#') + +/* Evaluates to 1 if C is one of the shell's special parameters for which an + indirect variable reference may be made. */ +#define VALID_INDIR_PARAM(c) \ + ((c) == '#' || (c) == '?' || (c) == '@' || (c) == '*') + +/* Evaluates to 1 if C is one of the OP characters that follows the parameter + in ${parameter[:]OPword}. */ +#define VALID_PARAM_EXPAND_CHAR(c) (sh_syntaxtab[(unsigned char)c] & CSUBSTOP) + +/* Evaluates to 1 if this is one of the shell's special variables. */ +#define SPECIAL_VAR(name, wi) \ + ((DIGIT (*name) && all_digits (name)) || \ + (name[1] == '\0' && (sh_syntaxtab[(unsigned char)*name] & CSPECVAR)) || \ + (wi && name[2] == '\0' && VALID_INDIR_PARAM (name[1]))) + +/* An expansion function that takes a string and a quoted flag and returns + a WORD_LIST *. Used as the type of the third argument to + expand_string_if_necessary(). */ +typedef WORD_LIST *EXPFUNC __P((char *, int)); + +/* Process ID of the last command executed within command substitution. */ +pid_t last_command_subst_pid = NO_PID; +pid_t current_command_subst_pid = NO_PID; + +/* Variables used to keep track of the characters in IFS. */ +SHELL_VAR *ifs_var; +char *ifs_value; +unsigned char ifs_cmap[UCHAR_MAX + 1]; + +#if defined (HANDLE_MULTIBYTE) +unsigned char ifs_firstc[MB_LEN_MAX]; +size_t ifs_firstc_len; +#else +unsigned char ifs_firstc; +#endif + +/* Sentinel to tell when we are performing variable assignments preceding a + command name and putting them into the environment. Used to make sure + we use the temporary environment when looking up variable values. */ +int assigning_in_environment; + +/* Used to hold a list of variable assignments preceding a command. Global + so the SIGCHLD handler in jobs.c can unwind-protect it when it runs a + SIGCHLD trap and so it can be saved and restored by the trap handlers. */ +WORD_LIST *subst_assign_varlist = (WORD_LIST *)NULL; + +/* Extern functions and variables from different files. */ +extern int last_command_exit_value, last_command_exit_signal; +extern int subshell_environment, line_number; +extern int subshell_level, parse_and_execute_level, sourcelevel; +extern int eof_encountered; +extern int return_catch_flag, return_catch_value; +extern pid_t dollar_dollar_pid; +extern int posixly_correct; +extern char *this_command_name; +extern struct fd_bitmap *current_fds_to_close; +extern int wordexp_only; +extern int expanding_redir; +extern int tempenv_assign_error; + +#if !defined (HAVE_WCSDUP) && defined (HANDLE_MULTIBYTE) +extern wchar_t *wcsdup __P((const wchar_t *)); +#endif + +/* Non-zero means to allow unmatched globbed filenames to expand to + a null file. */ +int allow_null_glob_expansion; + +/* Non-zero means to throw an error when globbing fails to match anything. */ +int fail_glob_expansion; + +#if 0 +/* Variables to keep track of which words in an expanded word list (the + output of expand_word_list_internal) are the result of globbing + expansions. GLOB_ARGV_FLAGS is used by execute_cmd.c. + (CURRENTLY UNUSED). */ +char *glob_argv_flags; +static int glob_argv_flags_size; +#endif + +static WORD_LIST expand_word_error, expand_word_fatal; +static WORD_DESC expand_wdesc_error, expand_wdesc_fatal; +static char expand_param_error, expand_param_fatal; +static char extract_string_error, extract_string_fatal; + +/* Tell the expansion functions to not longjmp back to top_level on fatal + errors. Enabled when doing completion and prompt string expansion. */ +static int no_longjmp_on_fatal_error = 0; + +/* Set by expand_word_unsplit; used to inhibit splitting and re-joining + $* on $IFS, primarily when doing assignment statements. */ +static int expand_no_split_dollar_star = 0; + +/* A WORD_LIST of words to be expanded by expand_word_list_internal, + without any leading variable assignments. */ +static WORD_LIST *garglist = (WORD_LIST *)NULL; + +static char *quoted_substring __P((char *, int, int)); +static int quoted_strlen __P((char *)); +static char *quoted_strchr __P((char *, int, int)); + +static char *expand_string_if_necessary __P((char *, int, EXPFUNC *)); +static inline char *expand_string_to_string_internal __P((char *, int, EXPFUNC *)); +static WORD_LIST *call_expand_word_internal __P((WORD_DESC *, int, int, int *, int *)); +static WORD_LIST *expand_string_internal __P((char *, int)); +static WORD_LIST *expand_string_leave_quoted __P((char *, int)); +static WORD_LIST *expand_string_for_rhs __P((char *, int, int *, int *)); + +static WORD_LIST *list_quote_escapes __P((WORD_LIST *)); +static char *make_quoted_char __P((int)); +static WORD_LIST *quote_list __P((WORD_LIST *)); + +static int unquoted_substring __P((char *, char *)); +static int unquoted_member __P((int, char *)); + +#if defined (ARRAY_VARS) +static SHELL_VAR *do_compound_assignment __P((char *, char *, int)); +#endif +static int do_assignment_internal __P((const WORD_DESC *, int)); + +static char *string_extract_verbatim __P((char *, size_t, int *, char *, int)); +static char *string_extract __P((char *, int *, char *, int)); +static char *string_extract_double_quoted __P((char *, int *, int)); +static inline char *string_extract_single_quoted __P((char *, int *)); +static inline int skip_single_quoted __P((const char *, size_t, int)); +static int skip_double_quoted __P((char *, size_t, int)); +static char *extract_delimited_string __P((char *, int *, char *, char *, char *, int)); +static char *extract_dollar_brace_string __P((char *, int *, int, int)); +static int skip_matched_pair __P((const char *, int, int, int, int)); + +static char *pos_params __P((char *, int, int, int)); + +static unsigned char *mb_getcharlens __P((char *, int)); + +static char *remove_upattern __P((char *, char *, int)); +#if defined (HANDLE_MULTIBYTE) +static wchar_t *remove_wpattern __P((wchar_t *, size_t, wchar_t *, int)); +#endif +static char *remove_pattern __P((char *, char *, int)); + +static int match_pattern_char __P((char *, char *)); +static int match_upattern __P((char *, char *, int, char **, char **)); +#if defined (HANDLE_MULTIBYTE) +static int match_pattern_wchar __P((wchar_t *, wchar_t *)); +static int match_wpattern __P((wchar_t *, char **, size_t, wchar_t *, int, char **, char **)); +#endif +static int match_pattern __P((char *, char *, int, char **, char **)); +static int getpatspec __P((int, char *)); +static char *getpattern __P((char *, int, int)); +static char *variable_remove_pattern __P((char *, char *, int, int)); +static char *list_remove_pattern __P((WORD_LIST *, char *, int, int, int)); +static char *parameter_list_remove_pattern __P((int, char *, int, int)); +#ifdef ARRAY_VARS +static char *array_remove_pattern __P((SHELL_VAR *, char *, int, char *, int)); +#endif +static char *parameter_brace_remove_pattern __P((char *, char *, int, char *, int, int, int)); + +static char *process_substitute __P((char *, int)); + +static char *read_comsub __P((int, int, int *)); + +#ifdef ARRAY_VARS +static arrayind_t array_length_reference __P((char *)); +#endif + +static int valid_brace_expansion_word __P((char *, int)); +static int chk_atstar __P((char *, int, int *, int *)); +static int chk_arithsub __P((const char *, int)); + +static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int, arrayind_t *)); +static WORD_DESC *parameter_brace_expand_indir __P((char *, int, int, int *, int *)); +static WORD_DESC *parameter_brace_expand_rhs __P((char *, char *, int, int, int *, int *)); +static void parameter_brace_expand_error __P((char *, char *)); + +static int valid_length_expression __P((char *)); +static intmax_t parameter_brace_expand_length __P((char *)); + +static char *skiparith __P((char *, int)); +static int verify_substring_values __P((SHELL_VAR *, char *, char *, int, intmax_t *, intmax_t *)); +static int get_var_and_type __P((char *, char *, arrayind_t, int, int, SHELL_VAR **, char **)); +static char *mb_substring __P((char *, int, int)); +static char *parameter_brace_substring __P((char *, char *, int, char *, int, int)); + +static int shouldexp_replacement __P((char *)); + +static char *pos_params_pat_subst __P((char *, char *, char *, int)); + +static char *parameter_brace_patsub __P((char *, char *, int, char *, int, int)); + +static char *pos_params_casemod __P((char *, char *, int, int)); +static char *parameter_brace_casemod __P((char *, char *, int, int, char *, int, int)); + +static WORD_DESC *parameter_brace_expand __P((char *, int *, int, int, int *, int *)); +static WORD_DESC *param_expand __P((char *, int *, int, int *, int *, int *, int *, int)); + +static WORD_LIST *expand_word_internal __P((WORD_DESC *, int, int, int *, int *)); + +static WORD_LIST *word_list_split __P((WORD_LIST *)); + +static void exp_jump_to_top_level __P((int)); + +static WORD_LIST *separate_out_assignments __P((WORD_LIST *)); +static WORD_LIST *glob_expand_word_list __P((WORD_LIST *, int)); +#ifdef BRACE_EXPANSION +static WORD_LIST *brace_expand_word_list __P((WORD_LIST *, int)); +#endif +#if defined (ARRAY_VARS) +static int make_internal_declare __P((char *, char *)); +#endif +static WORD_LIST *shell_expand_word_list __P((WORD_LIST *, int)); +static WORD_LIST *expand_word_list_internal __P((WORD_LIST *, int)); + +/* **************************************************************** */ +/* */ +/* Utility Functions */ +/* */ +/* **************************************************************** */ + +#if defined (DEBUG) +void +dump_word_flags (flags) + int flags; +{ + int f; + + f = flags; + fprintf (stderr, "%d -> ", f); + if (f & W_ASSIGNASSOC) + { + f &= ~W_ASSIGNASSOC; + fprintf (stderr, "W_ASSIGNASSOC%s", f ? "|" : ""); + } + if (f & W_HASCTLESC) + { + f &= ~W_HASCTLESC; + fprintf (stderr, "W_HASCTLESC%s", f ? "|" : ""); + } + if (f & W_NOPROCSUB) + { + f &= ~W_NOPROCSUB; + fprintf (stderr, "W_NOPROCSUB%s", f ? "|" : ""); + } + if (f & W_DQUOTE) + { + f &= ~W_DQUOTE; + fprintf (stderr, "W_DQUOTE%s", f ? "|" : ""); + } + if (f & W_HASQUOTEDNULL) + { + f &= ~W_HASQUOTEDNULL; + fprintf (stderr, "W_HASQUOTEDNULL%s", f ? "|" : ""); + } + if (f & W_ASSIGNARG) + { + f &= ~W_ASSIGNARG; + fprintf (stderr, "W_ASSIGNARG%s", f ? "|" : ""); + } + if (f & W_ASSNBLTIN) + { + f &= ~W_ASSNBLTIN; + fprintf (stderr, "W_ASSNBLTIN%s", f ? "|" : ""); + } + if (f & W_COMPASSIGN) + { + f &= ~W_COMPASSIGN; + fprintf (stderr, "W_COMPASSIGN%s", f ? "|" : ""); + } + if (f & W_NOEXPAND) + { + f &= ~W_NOEXPAND; + fprintf (stderr, "W_NOEXPAND%s", f ? "|" : ""); + } + if (f & W_ITILDE) + { + f &= ~W_ITILDE; + fprintf (stderr, "W_ITILDE%s", f ? "|" : ""); + } + if (f & W_NOTILDE) + { + f &= ~W_NOTILDE; + fprintf (stderr, "W_NOTILDE%s", f ? "|" : ""); + } + if (f & W_ASSIGNRHS) + { + f &= ~W_ASSIGNRHS; + fprintf (stderr, "W_ASSIGNRHS%s", f ? "|" : ""); + } + if (f & W_NOCOMSUB) + { + f &= ~W_NOCOMSUB; + fprintf (stderr, "W_NOCOMSUB%s", f ? "|" : ""); + } + if (f & W_DOLLARSTAR) + { + f &= ~W_DOLLARSTAR; + fprintf (stderr, "W_DOLLARSTAR%s", f ? "|" : ""); + } + if (f & W_DOLLARAT) + { + f &= ~W_DOLLARAT; + fprintf (stderr, "W_DOLLARAT%s", f ? "|" : ""); + } + if (f & W_TILDEEXP) + { + f &= ~W_TILDEEXP; + fprintf (stderr, "W_TILDEEXP%s", f ? "|" : ""); + } + if (f & W_NOSPLIT2) + { + f &= ~W_NOSPLIT2; + fprintf (stderr, "W_NOSPLIT2%s", f ? "|" : ""); + } + if (f & W_NOGLOB) + { + f &= ~W_NOGLOB; + fprintf (stderr, "W_NOGLOB%s", f ? "|" : ""); + } + if (f & W_NOSPLIT) + { + f &= ~W_NOSPLIT; + fprintf (stderr, "W_NOSPLIT%s", f ? "|" : ""); + } + if (f & W_GLOBEXP) + { + f &= ~W_GLOBEXP; + fprintf (stderr, "W_GLOBEXP%s", f ? "|" : ""); + } + if (f & W_ASSIGNMENT) + { + f &= ~W_ASSIGNMENT; + fprintf (stderr, "W_ASSIGNMENT%s", f ? "|" : ""); + } + if (f & W_QUOTED) + { + f &= ~W_QUOTED; + fprintf (stderr, "W_QUOTED%s", f ? "|" : ""); + } + if (f & W_HASDOLLAR) + { + f &= ~W_HASDOLLAR; + fprintf (stderr, "W_HASDOLLAR%s", f ? "|" : ""); + } + fprintf (stderr, "\n"); + fflush (stderr); +} +#endif + +#ifdef INCLUDE_UNUSED +static char * +quoted_substring (string, start, end) + char *string; + int start, end; +{ + register int len, l; + register char *result, *s, *r; + + len = end - start; + + /* Move to string[start], skipping quoted characters. */ + for (s = string, l = 0; *s && l < start; ) + { + if (*s == CTLESC) + { + s++; + continue; + } + l++; + if (*s == 0) + break; + } + + r = result = (char *)xmalloc (2*len + 1); /* save room for quotes */ + + /* Copy LEN characters, including quote characters. */ + s = string + l; + for (l = 0; l < len; s++) + { + if (*s == CTLESC) + *r++ = *s++; + *r++ = *s; + l++; + if (*s == 0) + break; + } + *r = '\0'; + return result; +} +#endif + +#ifdef INCLUDE_UNUSED +/* Return the length of S, skipping over quoted characters */ +static int +quoted_strlen (s) + char *s; +{ + register char *p; + int i; + + i = 0; + for (p = s; *p; p++) + { + if (*p == CTLESC) + { + p++; + if (*p == 0) + return (i + 1); + } + i++; + } + + return i; +} +#endif + +/* Find the first occurrence of character C in string S, obeying shell + quoting rules. If (FLAGS & ST_BACKSL) is non-zero, backslash-escaped + characters are skipped. If (FLAGS & ST_CTLESC) is non-zero, characters + escaped with CTLESC are skipped. */ +static char * +quoted_strchr (s, c, flags) + char *s; + int c, flags; +{ + register char *p; + + for (p = s; *p; p++) + { + if (((flags & ST_BACKSL) && *p == '\\') + || ((flags & ST_CTLESC) && *p == CTLESC)) + { + p++; + if (*p == '\0') + return ((char *)NULL); + continue; + } + else if (*p == c) + return p; + } + return ((char *)NULL); +} + +/* Return 1 if CHARACTER appears in an unquoted portion of + STRING. Return 0 otherwise. CHARACTER must be a single-byte character. */ +static int +unquoted_member (character, string) + int character; + char *string; +{ + size_t slen; + int sindex, c; + DECLARE_MBSTATE; + + slen = strlen (string); + sindex = 0; + while (c = string[sindex]) + { + if (c == character) + return (1); + + switch (c) + { + default: + ADVANCE_CHAR (string, slen, sindex); + break; + + case '\\': + sindex++; + if (string[sindex]) + ADVANCE_CHAR (string, slen, sindex); + break; + + case '\'': + sindex = skip_single_quoted (string, slen, ++sindex); + break; + + case '"': + sindex = skip_double_quoted (string, slen, ++sindex); + break; + } + } + return (0); +} + +/* Return 1 if SUBSTR appears in an unquoted portion of STRING. */ +static int +unquoted_substring (substr, string) + char *substr, *string; +{ + size_t slen; + int sindex, c, sublen; + DECLARE_MBSTATE; + + if (substr == 0 || *substr == '\0') + return (0); + + slen = strlen (string); + sublen = strlen (substr); + for (sindex = 0; c = string[sindex]; ) + { + if (STREQN (string + sindex, substr, sublen)) + return (1); + + switch (c) + { + case '\\': + sindex++; + + if (string[sindex]) + ADVANCE_CHAR (string, slen, sindex); + break; + + case '\'': + sindex = skip_single_quoted (string, slen, ++sindex); + break; + + case '"': + sindex = skip_double_quoted (string, slen, ++sindex); + break; + + default: + ADVANCE_CHAR (string, slen, sindex); + break; + } + } + return (0); +} + +/* Most of the substitutions must be done in parallel. In order + to avoid using tons of unclear goto's, I have some functions + for manipulating malloc'ed strings. They all take INDX, a + pointer to an integer which is the offset into the string + where manipulation is taking place. They also take SIZE, a + pointer to an integer which is the current length of the + character array for this string. */ + +/* Append SOURCE to TARGET at INDEX. SIZE is the current amount + of space allocated to TARGET. SOURCE can be NULL, in which + case nothing happens. Gets rid of SOURCE by freeing it. + Returns TARGET in case the location has changed. */ +INLINE char * +sub_append_string (source, target, indx, size) + char *source, *target; + int *indx, *size; +{ + if (source) + { + int srclen, n; + + srclen = STRLEN (source); + if (srclen >= (int)(*size - *indx)) + { + n = srclen + *indx; + n = (n + DEFAULT_ARRAY_SIZE) - (n % DEFAULT_ARRAY_SIZE); + target = (char *)xrealloc (target, (*size = n)); + } + + FASTCOPY (source, target + *indx, srclen); + *indx += srclen; + target[*indx] = '\0'; + + free (source); + } + return (target); +} + +#if 0 +/* UNUSED */ +/* Append the textual representation of NUMBER to TARGET. + INDX and SIZE are as in SUB_APPEND_STRING. */ +char * +sub_append_number (number, target, indx, size) + intmax_t number; + int *indx, *size; + char *target; +{ + char *temp; + + temp = itos (number); + return (sub_append_string (temp, target, indx, size)); +} +#endif + +/* Extract a substring from STRING, starting at SINDEX and ending with + one of the characters in CHARLIST. Don't make the ending character + part of the string. Leave SINDEX pointing at the ending character. + Understand about backslashes in the string. If (flags & SX_VARNAME) + is non-zero, and array variables have been compiled into the shell, + everything between a `[' and a corresponding `]' is skipped over. + If (flags & SX_NOALLOC) is non-zero, don't return the substring, just + update SINDEX. If (flags & SX_REQMATCH) is non-zero, the string must + contain a closing character from CHARLIST. */ +static char * +string_extract (string, sindex, charlist, flags) + char *string; + int *sindex; + char *charlist; + int flags; +{ + register int c, i; + int found; + size_t slen; + char *temp; + DECLARE_MBSTATE; + + slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 0; + i = *sindex; + found = 0; + while (c = string[i]) + { + if (c == '\\') + { + if (string[i + 1]) + i++; + else + break; + } +#if defined (ARRAY_VARS) + else if ((flags & SX_VARNAME) && c == '[') + { + int ni; + /* If this is an array subscript, skip over it and continue. */ + ni = skipsubscript (string, i, 0); + if (string[ni] == ']') + i = ni; + } +#endif + else if (MEMBER (c, charlist)) + { + found = 1; + break; + } + + ADVANCE_CHAR (string, slen, i); + } + + /* If we had to have a matching delimiter and didn't find one, return an + error and let the caller deal with it. */ + if ((flags & SX_REQMATCH) && found == 0) + { + *sindex = i; + return (&extract_string_error); + } + + temp = (flags & SX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i); + *sindex = i; + + return (temp); +} + +/* Extract the contents of STRING as if it is enclosed in double quotes. + SINDEX, when passed in, is the offset of the character immediately + following the opening double quote; on exit, SINDEX is left pointing after + the closing double quote. If STRIPDQ is non-zero, unquoted double + quotes are stripped and the string is terminated by a null byte. + Backslashes between the embedded double quotes are processed. If STRIPDQ + is zero, an unquoted `"' terminates the string. */ +static char * +string_extract_double_quoted (string, sindex, stripdq) + char *string; + int *sindex, stripdq; +{ + size_t slen; + char *send; + int j, i, t; + unsigned char c; + char *temp, *ret; /* The new string we return. */ + int pass_next, backquote, si; /* State variables for the machine. */ + int dquote; + DECLARE_MBSTATE; + + slen = strlen (string + *sindex) + *sindex; + send = string + slen; + + pass_next = backquote = dquote = 0; + temp = (char *)xmalloc (1 + slen - *sindex); + + j = 0; + i = *sindex; + while (c = string[i]) + { + /* Process a character that was quoted by a backslash. */ + if (pass_next) + { + /* XXX - take another look at this in light of Interp 221 */ + /* Posix.2 sez: + + ``The backslash shall retain its special meaning as an escape + character only when followed by one of the characters: + $ ` " \ <newline>''. + + If STRIPDQ is zero, we handle the double quotes here and let + expand_word_internal handle the rest. If STRIPDQ is non-zero, + we have already been through one round of backslash stripping, + and want to strip these backslashes only if DQUOTE is non-zero, + indicating that we are inside an embedded double-quoted string. */ + + /* If we are in an embedded quoted string, then don't strip + backslashes before characters for which the backslash + retains its special meaning, but remove backslashes in + front of other characters. If we are not in an + embedded quoted string, don't strip backslashes at all. + This mess is necessary because the string was already + surrounded by double quotes (and sh has some really weird + quoting rules). + The returned string will be run through expansion as if + it were double-quoted. */ + if ((stripdq == 0 && c != '"') || + (stripdq && ((dquote && (sh_syntaxtab[c] & CBSDQUOTE)) || dquote == 0))) + temp[j++] = '\\'; + pass_next = 0; + +add_one_character: + COPY_CHAR_I (temp, j, string, send, i); + continue; + } + + /* A backslash protects the next character. The code just above + handles preserving the backslash in front of any character but + a double quote. */ + if (c == '\\') + { + pass_next++; + i++; + continue; + } + + /* Inside backquotes, ``the portion of the quoted string from the + initial backquote and the characters up to the next backquote + that is not preceded by a backslash, having escape characters + removed, defines that command''. */ + if (backquote) + { + if (c == '`') + backquote = 0; + temp[j++] = c; + i++; + continue; + } + + if (c == '`') + { + temp[j++] = c; + backquote++; + i++; + continue; + } + + /* Pass everything between `$(' and the matching `)' or a quoted + ${ ... } pair through according to the Posix.2 specification. */ + if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE))) + { + int free_ret = 1; + + si = i + 2; + if (string[i + 1] == LPAREN) + ret = extract_command_subst (string, &si, 0); + else + ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, 0); + + temp[j++] = '$'; + temp[j++] = string[i + 1]; + + /* Just paranoia; ret will not be 0 unless no_longjmp_on_fatal_error + is set. */ + if (ret == 0 && no_longjmp_on_fatal_error) + { + free_ret = 0; + ret = string + i + 2; + } + + for (t = 0; ret[t]; t++, j++) + temp[j] = ret[t]; + temp[j] = string[si]; + + if (string[si]) + { + j++; + i = si + 1; + } + else + i = si; + + if (free_ret) + free (ret); + continue; + } + + /* Add any character but a double quote to the quoted string we're + accumulating. */ + if (c != '"') + goto add_one_character; + + /* c == '"' */ + if (stripdq) + { + dquote ^= 1; + i++; + continue; + } + + break; + } + temp[j] = '\0'; + + /* Point to after the closing quote. */ + if (c) + i++; + *sindex = i; + + return (temp); +} + +/* This should really be another option to string_extract_double_quoted. */ +static int +skip_double_quoted (string, slen, sind) + char *string; + size_t slen; + int sind; +{ + int c, i; + char *ret; + int pass_next, backquote, si; + DECLARE_MBSTATE; + + pass_next = backquote = 0; + i = sind; + while (c = string[i]) + { + if (pass_next) + { + pass_next = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next++; + i++; + continue; + } + else if (backquote) + { + if (c == '`') + backquote = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '`') + { + backquote++; + i++; + continue; + } + else if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE))) + { + si = i + 2; + if (string[i + 1] == LPAREN) + ret = extract_command_subst (string, &si, SX_NOALLOC); + else + ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC); + + i = si + 1; + continue; + } + else if (c != '"') + { + ADVANCE_CHAR (string, slen, i); + continue; + } + else + break; + } + + if (c) + i++; + + return (i); +} + +/* Extract the contents of STRING as if it is enclosed in single quotes. + SINDEX, when passed in, is the offset of the character immediately + following the opening single quote; on exit, SINDEX is left pointing after + the closing single quote. */ +static inline char * +string_extract_single_quoted (string, sindex) + char *string; + int *sindex; +{ + register int i; + size_t slen; + char *t; + DECLARE_MBSTATE; + + /* Don't need slen for ADVANCE_CHAR unless multibyte chars possible. */ + slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 0; + i = *sindex; + while (string[i] && string[i] != '\'') + ADVANCE_CHAR (string, slen, i); + + t = substring (string, *sindex, i); + + if (string[i]) + i++; + *sindex = i; + + return (t); +} + +static inline int +skip_single_quoted (string, slen, sind) + const char *string; + size_t slen; + int sind; +{ + register int c; + DECLARE_MBSTATE; + + c = sind; + while (string[c] && string[c] != '\'') + ADVANCE_CHAR (string, slen, c); + + if (string[c]) + c++; + return c; +} + +/* Just like string_extract, but doesn't hack backslashes or any of + that other stuff. Obeys CTLESC quoting. Used to do splitting on $IFS. */ +static char * +string_extract_verbatim (string, slen, sindex, charlist, flags) + char *string; + size_t slen; + int *sindex; + char *charlist; + int flags; +{ + register int i; +#if defined (HANDLE_MULTIBYTE) + size_t clen; + wchar_t *wcharlist; +#endif + int c; + char *temp; + DECLARE_MBSTATE; + + if (charlist[0] == '\'' && charlist[1] == '\0') + { + temp = string_extract_single_quoted (string, sindex); + --*sindex; /* leave *sindex at separator character */ + return temp; + } + + i = *sindex; +#if 0 + /* See how the MBLEN and ADVANCE_CHAR macros work to understand why we need + this only if MB_CUR_MAX > 1. */ + slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 1; +#endif +#if defined (HANDLE_MULTIBYTE) + clen = strlen (charlist); + wcharlist = 0; +#endif + while (c = string[i]) + { +#if defined (HANDLE_MULTIBYTE) + size_t mblength; +#endif + if ((flags & SX_NOCTLESC) == 0 && c == CTLESC) + { + i += 2; + continue; + } + /* Even if flags contains SX_NOCTLESC, we let CTLESC quoting CTLNUL + through, to protect the CTLNULs from later calls to + remove_quoted_nulls. */ + else if ((flags & SX_NOESCCTLNUL) == 0 && c == CTLESC && string[i+1] == CTLNUL) + { + i += 2; + continue; + } + +#if defined (HANDLE_MULTIBYTE) + mblength = MBLEN (string + i, slen - i); + if (mblength > 1) + { + wchar_t wc; + mblength = mbtowc (&wc, string + i, slen - i); + if (MB_INVALIDCH (mblength)) + { + if (MEMBER (c, charlist)) + break; + } + else + { + if (wcharlist == 0) + { + size_t len; + len = mbstowcs (wcharlist, charlist, 0); + if (len == -1) + len = 0; + wcharlist = (wchar_t *)xmalloc (sizeof (wchar_t) * (len + 1)); + mbstowcs (wcharlist, charlist, len + 1); + } + + if (wcschr (wcharlist, wc)) + break; + } + } + else +#endif + if (MEMBER (c, charlist)) + break; + + ADVANCE_CHAR (string, slen, i); + } + +#if defined (HANDLE_MULTIBYTE) + FREE (wcharlist); +#endif + + temp = substring (string, *sindex, i); + *sindex = i; + + return (temp); +} + +/* Extract the $( construct in STRING, and return a new string. + Start extracting at (SINDEX) as if we had just seen "$(". + Make (SINDEX) get the position of the matching ")". ) + XFLAGS is additional flags to pass to other extraction functions. */ +char * +extract_command_subst (string, sindex, xflags) + char *string; + int *sindex; + int xflags; +{ + if (string[*sindex] == LPAREN) + return (extract_delimited_string (string, sindex, "$(", "(", ")", xflags|SX_COMMAND)); /*)*/ + else + { + xflags |= (no_longjmp_on_fatal_error ? SX_NOLONGJMP : 0); + return (xparse_dolparen (string, string+*sindex, sindex, xflags)); + } +} + +/* Extract the $[ construct in STRING, and return a new string. (]) + Start extracting at (SINDEX) as if we had just seen "$[". + Make (SINDEX) get the position of the matching "]". */ +char * +extract_arithmetic_subst (string, sindex) + char *string; + int *sindex; +{ + return (extract_delimited_string (string, sindex, "$[", "[", "]", 0)); /*]*/ +} + +#if defined (PROCESS_SUBSTITUTION) +/* Extract the <( or >( construct in STRING, and return a new string. + Start extracting at (SINDEX) as if we had just seen "<(". + Make (SINDEX) get the position of the matching ")". */ /*))*/ +char * +extract_process_subst (string, starter, sindex) + char *string; + char *starter; + int *sindex; +{ + return (extract_delimited_string (string, sindex, starter, "(", ")", 0)); +} +#endif /* PROCESS_SUBSTITUTION */ + +#if defined (ARRAY_VARS) +/* This can be fooled by unquoted right parens in the passed string. If + each caller verifies that the last character in STRING is a right paren, + we don't even need to call extract_delimited_string. */ +char * +extract_array_assignment_list (string, sindex) + char *string; + int *sindex; +{ + int slen; + char *ret; + + slen = strlen (string); /* ( */ + if (string[slen - 1] == ')') + { + ret = substring (string, *sindex, slen - 1); + *sindex = slen - 1; + return ret; + } + return 0; +} +#endif + +/* Extract and create a new string from the contents of STRING, a + character string delimited with OPENER and CLOSER. SINDEX is + the address of an int describing the current offset in STRING; + it should point to just after the first OPENER found. On exit, + SINDEX gets the position of the last character of the matching CLOSER. + If OPENER is more than a single character, ALT_OPENER, if non-null, + contains a character string that can also match CLOSER and thus + needs to be skipped. */ +static char * +extract_delimited_string (string, sindex, opener, alt_opener, closer, flags) + char *string; + int *sindex; + char *opener, *alt_opener, *closer; + int flags; +{ + int i, c, si; + size_t slen; + char *t, *result; + int pass_character, nesting_level, in_comment; + int len_closer, len_opener, len_alt_opener; + DECLARE_MBSTATE; + + slen = strlen (string + *sindex) + *sindex; + len_opener = STRLEN (opener); + len_alt_opener = STRLEN (alt_opener); + len_closer = STRLEN (closer); + + pass_character = in_comment = 0; + + nesting_level = 1; + i = *sindex; + + while (nesting_level) + { + c = string[i]; + + if (c == 0) + break; + + if (in_comment) + { + if (c == '\n') + in_comment = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + + if (pass_character) /* previous char was backslash */ + { + pass_character = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + + /* Not exactly right yet; should handle shell metacharacters and + multibyte characters, too. See COMMENT_BEGIN define in parse.y */ + if ((flags & SX_COMMAND) && c == '#' && (i == 0 || string[i - 1] == '\n' || shellblank (string[i - 1]))) + { + in_comment = 1; + ADVANCE_CHAR (string, slen, i); + continue; + } + + if (c == CTLESC || c == '\\') + { + pass_character++; + i++; + continue; + } + + /* Process a nested command substitution, but only if we're parsing an + arithmetic substitution. */ + if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LPAREN) + { + si = i + 2; + t = extract_command_subst (string, &si, flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Process a nested OPENER. */ + if (STREQN (string + i, opener, len_opener)) + { + si = i + len_opener; + t = extract_delimited_string (string, &si, opener, alt_opener, closer, flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Process a nested ALT_OPENER */ + if (len_alt_opener && STREQN (string + i, alt_opener, len_alt_opener)) + { + si = i + len_alt_opener; + t = extract_delimited_string (string, &si, alt_opener, alt_opener, closer, flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* If the current substring terminates the delimited string, decrement + the nesting level. */ + if (STREQN (string + i, closer, len_closer)) + { + i += len_closer - 1; /* move to last byte of the closer */ + nesting_level--; + if (nesting_level == 0) + break; + } + + /* Pass old-style command substitution through verbatim. */ + if (c == '`') + { + si = i + 1; + t = string_extract (string, &si, "`", flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Pass single-quoted and double-quoted strings through verbatim. */ + if (c == '\'' || c == '"') + { + si = i + 1; + i = (c == '\'') ? skip_single_quoted (string, slen, si) + : skip_double_quoted (string, slen, si); + continue; + } + + /* move past this character, which was not special. */ + ADVANCE_CHAR (string, slen, i); + } + + if (c == 0 && nesting_level) + { + if (no_longjmp_on_fatal_error == 0) + { + report_error (_("bad substitution: no closing `%s' in %s"), closer, string); + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level (DISCARD); + } + else + { + *sindex = i; + return (char *)NULL; + } + } + + si = i - *sindex - len_closer + 1; + if (flags & SX_NOALLOC) + result = (char *)NULL; + else + { + result = (char *)xmalloc (1 + si); + strncpy (result, string + *sindex, si); + result[si] = '\0'; + } + *sindex = i; + + return (result); +} + +/* Extract a parameter expansion expression within ${ and } from STRING. + Obey the Posix.2 rules for finding the ending `}': count braces while + skipping over enclosed quoted strings and command substitutions. + SINDEX is the address of an int describing the current offset in STRING; + it should point to just after the first `{' found. On exit, SINDEX + gets the position of the matching `}'. QUOTED is non-zero if this + occurs inside double quotes. */ +/* XXX -- this is very similar to extract_delimited_string -- XXX */ +static char * +extract_dollar_brace_string (string, sindex, quoted, flags) + char *string; + int *sindex, quoted, flags; +{ + register int i, c; + size_t slen; + int pass_character, nesting_level, si, dolbrace_state; + char *result, *t; + DECLARE_MBSTATE; + + pass_character = 0; + nesting_level = 1; + slen = strlen (string + *sindex) + *sindex; + + /* The handling of dolbrace_state needs to agree with the code in parse.y: + parse_matched_pair() */ + dolbrace_state = 0; + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + dolbrace_state = (flags & SX_POSIXEXP) ? DOLBRACE_QUOTE : DOLBRACE_PARAM; + + i = *sindex; + while (c = string[i]) + { + if (pass_character) + { + pass_character = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + + /* CTLESCs and backslashes quote the next character. */ + if (c == CTLESC || c == '\\') + { + pass_character++; + i++; + continue; + } + + if (string[i] == '$' && string[i+1] == LBRACE) + { + nesting_level++; + i += 2; + continue; + } + + if (c == RBRACE) + { + nesting_level--; + if (nesting_level == 0) + break; + i++; + continue; + } + + /* Pass the contents of old-style command substitutions through + verbatim. */ + if (c == '`') + { + si = i + 1; + t = string_extract (string, &si, "`", flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Pass the contents of new-style command substitutions and + arithmetic substitutions through verbatim. */ + if (string[i] == '$' && string[i+1] == LPAREN) + { + si = i + 2; + t = extract_command_subst (string, &si, flags|SX_NOALLOC); + i = si + 1; + continue; + } + +#if 0 + /* Pass the contents of single-quoted and double-quoted strings + through verbatim. */ + if (c == '\'' || c == '"') + { + si = i + 1; + i = (c == '\'') ? skip_single_quoted (string, slen, si) + : skip_double_quoted (string, slen, si); + /* skip_XXX_quoted leaves index one past close quote */ + continue; + } +#else /* XXX - bash-4.2 */ + /* Pass the contents of double-quoted strings through verbatim. */ + if (c == '"') + { + si = i + 1; + i = skip_double_quoted (string, slen, si); + /* skip_XXX_quoted leaves index one past close quote */ + continue; + } + + if (c == '\'') + { +/*itrace("extract_dollar_brace_string: c == single quote flags = %d quoted = %d dolbrace_state = %d", flags, quoted, dolbrace_state);*/ + if (posixly_correct && shell_compatibility_level > 41 && dolbrace_state != DOLBRACE_QUOTE) + ADVANCE_CHAR (string, slen, i); + else + { + si = i + 1; + i = skip_single_quoted (string, slen, si); + } + + continue; + } +#endif + + /* move past this character, which was not special. */ + ADVANCE_CHAR (string, slen, i); + + /* This logic must agree with parse.y:parse_matched_pair, since they + share the same defines. */ + if (dolbrace_state == DOLBRACE_PARAM && c == '%' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == '#' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == '/' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == '^' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == ',' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", c) != 0) + dolbrace_state = DOLBRACE_OP; + else if (dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", c) == 0) + dolbrace_state = DOLBRACE_WORD; + } + + if (c == 0 && nesting_level) + { + if (no_longjmp_on_fatal_error == 0) + { /* { */ + report_error (_("bad substitution: no closing `%s' in %s"), "}", string); + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level (DISCARD); + } + else + { + *sindex = i; + return ((char *)NULL); + } + } + + result = (flags & SX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i); + *sindex = i; + + return (result); +} + +/* Remove backslashes which are quoting backquotes from STRING. Modifies + STRING, and returns a pointer to it. */ +char * +de_backslash (string) + char *string; +{ + register size_t slen; + register int i, j, prev_i; + DECLARE_MBSTATE; + + slen = strlen (string); + i = j = 0; + + /* Loop copying string[i] to string[j], i >= j. */ + while (i < slen) + { + if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' || + string[i + 1] == '$')) + i++; + prev_i = i; + ADVANCE_CHAR (string, slen, i); + if (j < prev_i) + do string[j++] = string[prev_i++]; while (prev_i < i); + else + j = i; + } + string[j] = '\0'; + + return (string); +} + +#if 0 +/*UNUSED*/ +/* Replace instances of \! in a string with !. */ +void +unquote_bang (string) + char *string; +{ + register int i, j; + register char *temp; + + temp = (char *)xmalloc (1 + strlen (string)); + + for (i = 0, j = 0; (temp[j] = string[i]); i++, j++) + { + if (string[i] == '\\' && string[i + 1] == '!') + { + temp[j] = '!'; + i++; + } + } + strcpy (string, temp); + free (temp); +} +#endif + +#define CQ_RETURN(x) do { no_longjmp_on_fatal_error = 0; return (x); } while (0) + +/* This function assumes s[i] == open; returns with s[ret] == close; used to + parse array subscripts. FLAGS & 1 means to not attempt to skip over + matched pairs of quotes or backquotes, or skip word expansions; it is + intended to be used after expansion has been performed and during final + assignment parsing (see arrayfunc.c:assign_compound_array_list()). */ +static int +skip_matched_pair (string, start, open, close, flags) + const char *string; + int start, open, close, flags; +{ + int i, pass_next, backq, si, c, count; + size_t slen; + char *temp, *ss; + DECLARE_MBSTATE; + + slen = strlen (string + start) + start; + no_longjmp_on_fatal_error = 1; + + i = start + 1; /* skip over leading bracket */ + count = 1; + pass_next = backq = 0; + ss = (char *)string; + while (c = string[i]) + { + if (pass_next) + { + pass_next = 0; + if (c == 0) + CQ_RETURN(i); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (backq) + { + if (c == '`') + backq = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if ((flags & 1) == 0 && c == '`') + { + backq = 1; + i++; + continue; + } + else if ((flags & 1) == 0 && c == open) + { + count++; + i++; + continue; + } + else if (c == close) + { + count--; + if (count == 0) + break; + i++; + continue; + } + else if ((flags & 1) == 0 && (c == '\'' || c == '"')) + { + i = (c == '\'') ? skip_single_quoted (ss, slen, ++i) + : skip_double_quoted (ss, slen, ++i); + /* no increment, the skip functions increment past the closing quote. */ + } + else if ((flags&1) == 0 && c == '$' && (string[i+1] == LPAREN || string[i+1] == LBRACE)) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + + if (string[i+1] == LPAREN) + temp = extract_delimited_string (ss, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */ + else + temp = extract_dollar_brace_string (ss, &si, 0, SX_NOALLOC); + i = si; + if (string[i] == '\0') /* don't increment i past EOS in loop */ + break; + i++; + continue; + } + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(i); +} + +#if defined (ARRAY_VARS) +int +skipsubscript (string, start, flags) + const char *string; + int start, flags; +{ + return (skip_matched_pair (string, start, '[', ']', flags)); +} +#endif + +/* Skip characters in STRING until we find a character in DELIMS, and return + the index of that character. START is the index into string at which we + begin. This is similar in spirit to strpbrk, but it returns an index into + STRING and takes a starting index. This little piece of code knows quite + a lot of shell syntax. It's very similar to skip_double_quoted and other + functions of that ilk. */ +int +skip_to_delim (string, start, delims, flags) + char *string; + int start; + char *delims; + int flags; +{ + int i, pass_next, backq, si, c, invert, skipquote, skipcmd; + size_t slen; + char *temp; + DECLARE_MBSTATE; + + slen = strlen (string + start) + start; + if (flags & SD_NOJMP) + no_longjmp_on_fatal_error = 1; + invert = (flags & SD_INVERT); + skipcmd = (flags & SD_NOSKIPCMD) == 0; + + i = start; + pass_next = backq = 0; + while (c = string[i]) + { + /* If this is non-zero, we should not let quote characters be delimiters + and the current character is a single or double quote. We should not + test whether or not it's a delimiter until after we skip single- or + double-quoted strings. */ + skipquote = ((flags & SD_NOQUOTEDELIM) && (c == '\'' || c =='"')); + if (pass_next) + { + pass_next = 0; + if (c == 0) + CQ_RETURN(i); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (backq) + { + if (c == '`') + backq = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '`') + { + backq = 1; + i++; + continue; + } + else if (skipquote == 0 && invert == 0 && member (c, delims)) + break; + else if (c == '\'' || c == '"') + { + i = (c == '\'') ? skip_single_quoted (string, slen, ++i) + : skip_double_quoted (string, slen, ++i); + /* no increment, the skip functions increment past the closing quote. */ + } + else if (c == '$' && ((skipcmd && string[i+1] == LPAREN) || string[i+1] == LBRACE)) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + + if (string[i+1] == LPAREN) + temp = extract_delimited_string (string, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */ + else + temp = extract_dollar_brace_string (string, &si, 0, SX_NOALLOC); + i = si; + if (string[i] == '\0') /* don't increment i past EOS in loop */ + break; + i++; + continue; + } +#if defined (PROCESS_SUBSTITUTION) + else if (skipcmd && (c == '<' || c == '>') && string[i+1] == LPAREN) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + temp = extract_process_subst (string, (c == '<') ? "<(" : ">(", &si); + i = si; + if (string[i] == '\0') + break; + i++; + continue; + } +#endif /* PROCESS_SUBSTITUTION */ + else if ((skipquote || invert) && (member (c, delims) == 0)) + break; + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(i); +} + +#if defined (READLINE) +/* Return 1 if the portion of STRING ending at EINDEX is quoted (there is + an unclosed quoted string), or if the character at EINDEX is quoted + by a backslash. NO_LONGJMP_ON_FATAL_ERROR is used to flag that the various + single and double-quoted string parsing functions should not return an + error if there are unclosed quotes or braces. The characters that this + recognizes need to be the same as the contents of + rl_completer_quote_characters. */ + +int +char_is_quoted (string, eindex) + char *string; + int eindex; +{ + int i, pass_next, c; + size_t slen; + DECLARE_MBSTATE; + + slen = strlen (string); + no_longjmp_on_fatal_error = 1; + i = pass_next = 0; + while (i <= eindex) + { + c = string[i]; + + if (pass_next) + { + pass_next = 0; + if (i >= eindex) /* XXX was if (i >= eindex - 1) */ + CQ_RETURN(1); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (c == '\'' || c == '"') + { + i = (c == '\'') ? skip_single_quoted (string, slen, ++i) + : skip_double_quoted (string, slen, ++i); + if (i > eindex) + CQ_RETURN(1); + /* no increment, the skip_xxx functions go one past end */ + } + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(0); +} + +int +unclosed_pair (string, eindex, openstr) + char *string; + int eindex; + char *openstr; +{ + int i, pass_next, openc, olen; + size_t slen; + DECLARE_MBSTATE; + + slen = strlen (string); + olen = strlen (openstr); + i = pass_next = openc = 0; + while (i <= eindex) + { + if (pass_next) + { + pass_next = 0; + if (i >= eindex) /* XXX was if (i >= eindex - 1) */ + return 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (string[i] == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (STREQN (string + i, openstr, olen)) + { + openc = 1 - openc; + i += olen; + } + else if (string[i] == '\'' || string[i] == '"') + { + i = (string[i] == '\'') ? skip_single_quoted (string, slen, i) + : skip_double_quoted (string, slen, i); + if (i > eindex) + return 0; + } + else + ADVANCE_CHAR (string, slen, i); + } + return (openc); +} + +/* Split STRING (length SLEN) at DELIMS, and return a WORD_LIST with the + individual words. If DELIMS is NULL, the current value of $IFS is used + to split the string, and the function follows the shell field splitting + rules. SENTINEL is an index to look for. NWP, if non-NULL, + gets the number of words in the returned list. CWP, if non-NULL, gets + the index of the word containing SENTINEL. Non-whitespace chars in + DELIMS delimit separate fields. */ +WORD_LIST * +split_at_delims (string, slen, delims, sentinel, flags, nwp, cwp) + char *string; + int slen; + char *delims; + int sentinel, flags; + int *nwp, *cwp; +{ + int ts, te, i, nw, cw, ifs_split, dflags; + char *token, *d, *d2; + WORD_LIST *ret, *tl; + + if (string == 0 || *string == '\0') + { + if (nwp) + *nwp = 0; + if (cwp) + *cwp = 0; + return ((WORD_LIST *)NULL); + } + + d = (delims == 0) ? ifs_value : delims; + ifs_split = delims == 0; + + /* Make d2 the non-whitespace characters in delims */ + d2 = 0; + if (delims) + { + size_t slength; +#if defined (HANDLE_MULTIBYTE) + size_t mblength = 1; +#endif + DECLARE_MBSTATE; + + slength = strlen (delims); + d2 = (char *)xmalloc (slength + 1); + i = ts = 0; + while (delims[i]) + { +#if defined (HANDLE_MULTIBYTE) + mbstate_t state_bak; + state_bak = state; + mblength = MBRLEN (delims + i, slength, &state); + if (MB_INVALIDCH (mblength)) + state = state_bak; + else if (mblength > 1) + { + memcpy (d2 + ts, delims + i, mblength); + ts += mblength; + i += mblength; + slength -= mblength; + continue; + } +#endif + if (whitespace (delims[i]) == 0) + d2[ts++] = delims[i]; + + i++; + slength--; + } + d2[ts] = '\0'; + } + + ret = (WORD_LIST *)NULL; + + /* Remove sequences of whitespace characters at the start of the string, as + long as those characters are delimiters. */ + for (i = 0; member (string[i], d) && spctabnl (string[i]); i++) + ; + if (string[i] == '\0') + return (ret); + + ts = i; + nw = 0; + cw = -1; + dflags = flags|SD_NOJMP; + while (1) + { + te = skip_to_delim (string, ts, d, dflags); + + /* If we have a non-whitespace delimiter character, use it to make a + separate field. This is just about what $IFS splitting does and + is closer to the behavior of the shell parser. */ + if (ts == te && d2 && member (string[ts], d2)) + { + te = ts + 1; + /* If we're using IFS splitting, the non-whitespace delimiter char + and any additional IFS whitespace delimits a field. */ + if (ifs_split) + while (member (string[te], d) && spctabnl (string[te])) + te++; + else + while (member (string[te], d2)) + te++; + } + + token = substring (string, ts, te); + + ret = add_string_to_list (token, ret); + free (token); + nw++; + + if (sentinel >= ts && sentinel <= te) + cw = nw; + + /* If the cursor is at whitespace just before word start, set the + sentinel word to the current word. */ + if (cwp && cw == -1 && sentinel == ts-1) + cw = nw; + + /* If the cursor is at whitespace between two words, make a new, empty + word, add it before (well, after, since the list is in reverse order) + the word we just added, and set the current word to that one. */ + if (cwp && cw == -1 && sentinel < ts) + { + tl = make_word_list (make_word (""), ret->next); + ret->next = tl; + cw = nw; + nw++; + } + + if (string[te] == 0) + break; + + i = te; + while (member (string[i], d) && (ifs_split || spctabnl(string[i]))) + i++; + + if (string[i]) + ts = i; + else + break; + } + + /* Special case for SENTINEL at the end of STRING. If we haven't found + the word containing SENTINEL yet, and the index we're looking for is at + the end of STRING (or past the end of the previously-found token, + possible if the end of the line is composed solely of IFS whitespace) + add an additional null argument and set the current word pointer to that. */ + if (cwp && cw == -1 && (sentinel >= slen || sentinel >= te)) + { + if (whitespace (string[sentinel - 1])) + { + token = ""; + ret = add_string_to_list (token, ret); + nw++; + } + cw = nw; + } + + if (nwp) + *nwp = nw; + if (cwp) + *cwp = cw; + + return (REVERSE_LIST (ret, WORD_LIST *)); +} +#endif /* READLINE */ + +#if 0 +/* UNUSED */ +/* Extract the name of the variable to bind to from the assignment string. */ +char * +assignment_name (string) + char *string; +{ + int offset; + char *temp; + + offset = assignment (string, 0); + if (offset == 0) + return (char *)NULL; + temp = substring (string, 0, offset); + return (temp); +} +#endif + +/* **************************************************************** */ +/* */ +/* Functions to convert strings to WORD_LISTs and vice versa */ +/* */ +/* **************************************************************** */ + +/* Return a single string of all the words in LIST. SEP is the separator + to put between individual elements of LIST in the output string. */ +char * +string_list_internal (list, sep) + WORD_LIST *list; + char *sep; +{ + register WORD_LIST *t; + char *result, *r; + int word_len, sep_len, result_size; + + if (list == 0) + return ((char *)NULL); + + /* Short-circuit quickly if we don't need to separate anything. */ + if (list->next == 0) + return (savestring (list->word->word)); + + /* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */ + sep_len = STRLEN (sep); + result_size = 0; + + for (t = list; t; t = t->next) + { + if (t != list) + result_size += sep_len; + result_size += strlen (t->word->word); + } + + r = result = (char *)xmalloc (result_size + 1); + + for (t = list; t; t = t->next) + { + if (t != list && sep_len) + { + if (sep_len > 1) + { + FASTCOPY (sep, r, sep_len); + r += sep_len; + } + else + *r++ = sep[0]; + } + + word_len = strlen (t->word->word); + FASTCOPY (t->word->word, r, word_len); + r += word_len; + } + + *r = '\0'; + return (result); +} + +/* Return a single string of all the words present in LIST, separating + each word with a space. */ +char * +string_list (list) + WORD_LIST *list; +{ + return (string_list_internal (list, " ")); +} + +/* An external interface that can be used by the rest of the shell to + obtain a string containing the first character in $IFS. Handles all + the multibyte complications. If LENP is non-null, it is set to the + length of the returned string. */ +char * +ifs_firstchar (lenp) + int *lenp; +{ + char *ret; + int len; + + ret = xmalloc (MB_LEN_MAX + 1); +#if defined (HANDLE_MULTIBYTE) + if (ifs_firstc_len == 1) + { + ret[0] = ifs_firstc[0]; + ret[1] = '\0'; + len = ret[0] ? 1 : 0; + } + else + { + memcpy (ret, ifs_firstc, ifs_firstc_len); + ret[len = ifs_firstc_len] = '\0'; + } +#else + ret[0] = ifs_firstc; + ret[1] = '\0'; + len = ret[0] ? 0 : 1; +#endif + + if (lenp) + *lenp = len; + + return ret; +} + +/* Return a single string of all the words present in LIST, obeying the + quoting rules for "$*", to wit: (P1003.2, draft 11, 3.5.2) "If the + expansion [of $*] appears within a double quoted string, it expands + to a single field with the value of each parameter separated by the + first character of the IFS variable, or by a <space> if IFS is unset." */ +char * +string_list_dollar_star (list) + WORD_LIST *list; +{ + char *ret; +#if defined (HANDLE_MULTIBYTE) +# if defined (__GNUC__) + char sep[MB_CUR_MAX + 1]; +# else + char *sep = 0; +# endif +#else + char sep[2]; +#endif + +#if defined (HANDLE_MULTIBYTE) +# if !defined (__GNUC__) + sep = (char *)xmalloc (MB_CUR_MAX + 1); +# endif /* !__GNUC__ */ + if (ifs_firstc_len == 1) + { + sep[0] = ifs_firstc[0]; + sep[1] = '\0'; + } + else + { + memcpy (sep, ifs_firstc, ifs_firstc_len); + sep[ifs_firstc_len] = '\0'; + } +#else + sep[0] = ifs_firstc; + sep[1] = '\0'; +#endif + + ret = string_list_internal (list, sep); +#if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__) + free (sep); +#endif + return ret; +} + +/* Turn $@ into a string. If (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + is non-zero, the $@ appears within double quotes, and we should quote + the list before converting it into a string. If IFS is unset, and the + word is not quoted, we just need to quote CTLESC and CTLNUL characters + in the words in the list, because the default value of $IFS is + <space><tab><newline>, IFS characters in the words in the list should + also be split. If IFS is null, and the word is not quoted, we need + to quote the words in the list to preserve the positional parameters + exactly. */ +char * +string_list_dollar_at (list, quoted) + WORD_LIST *list; + int quoted; +{ + char *ifs, *ret; +#if defined (HANDLE_MULTIBYTE) +# if defined (__GNUC__) + char sep[MB_CUR_MAX + 1]; +# else + char *sep = 0; +# endif /* !__GNUC__ */ +#else + char sep[2]; +#endif + WORD_LIST *tlist; + + /* XXX this could just be ifs = ifs_value; */ + ifs = ifs_var ? value_cell (ifs_var) : (char *)0; + +#if defined (HANDLE_MULTIBYTE) +# if !defined (__GNUC__) + sep = (char *)xmalloc (MB_CUR_MAX + 1); +# endif /* !__GNUC__ */ + if (ifs && *ifs) + { + if (ifs_firstc_len == 1) + { + sep[0] = ifs_firstc[0]; + sep[1] = '\0'; + } + else + { + memcpy (sep, ifs_firstc, ifs_firstc_len); + sep[ifs_firstc_len] = '\0'; + } + } + else + { + sep[0] = ' '; + sep[1] = '\0'; + } +#else + sep[0] = (ifs == 0 || *ifs == 0) ? ' ' : *ifs; + sep[1] = '\0'; +#endif + + /* XXX -- why call quote_list if ifs == 0? we can get away without doing + it now that quote_escapes quotes spaces */ +#if 0 + tlist = ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (ifs && *ifs == 0)) +#else + tlist = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE)) +#endif + ? quote_list (list) + : list_quote_escapes (list); + + ret = string_list_internal (tlist, sep); +#if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__) + free (sep); +#endif + return ret; +} + +/* Turn the positional paramters into a string, understanding quoting and + the various subtleties of using the first character of $IFS as the + separator. Calls string_list_dollar_at, string_list_dollar_star, and + string_list as appropriate. */ +char * +string_list_pos_params (pchar, list, quoted) + int pchar; + WORD_LIST *list; + int quoted; +{ + char *ret; + WORD_LIST *tlist; + + if (pchar == '*' && (quoted & Q_DOUBLE_QUOTES)) + { + tlist = quote_list (list); + word_list_remove_quoted_nulls (tlist); + ret = string_list_dollar_star (tlist); + } + else if (pchar == '*' && (quoted & Q_HERE_DOCUMENT)) + { + tlist = quote_list (list); + word_list_remove_quoted_nulls (tlist); + ret = string_list (tlist); + } + else if (pchar == '*') + { + /* Even when unquoted, string_list_dollar_star does the right thing + making sure that the first character of $IFS is used as the + separator. */ + ret = string_list_dollar_star (list); + } + else if (pchar == '@' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + /* We use string_list_dollar_at, but only if the string is quoted, since + that quotes the escapes if it's not, which we don't want. We could + use string_list (the old code did), but that doesn't do the right + thing if the first character of $IFS is not a space. We use + string_list_dollar_star if the string is unquoted so we make sure that + the elements of $@ are separated by the first character of $IFS for + later splitting. */ + ret = string_list_dollar_at (list, quoted); + else if (pchar == '@') + ret = string_list_dollar_star (list); + else + ret = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (list) : list); + + return ret; +} + +/* Return the list of words present in STRING. Separate the string into + words at any of the characters found in SEPARATORS. If QUOTED is + non-zero then word in the list will have its quoted flag set, otherwise + the quoted flag is left as make_word () deemed fit. + + This obeys the P1003.2 word splitting semantics. If `separators' is + exactly <space><tab><newline>, then the splitting algorithm is that of + the Bourne shell, which treats any sequence of characters from `separators' + as a delimiter. If IFS is unset, which results in `separators' being set + to "", no splitting occurs. If separators has some other value, the + following rules are applied (`IFS white space' means zero or more + occurrences of <space>, <tab>, or <newline>, as long as those characters + are in `separators'): + + 1) IFS white space is ignored at the start and the end of the + string. + 2) Each occurrence of a character in `separators' that is not + IFS white space, along with any adjacent occurrences of + IFS white space delimits a field. + 3) Any nonzero-length sequence of IFS white space delimits a field. + */ + +/* BEWARE! list_string strips null arguments. Don't call it twice and + expect to have "" preserved! */ + +/* This performs word splitting and quoted null character removal on + STRING. */ +#define issep(c) \ + (((separators)[0]) ? ((separators)[1] ? isifs(c) \ + : (c) == (separators)[0]) \ + : 0) + +WORD_LIST * +list_string (string, separators, quoted) + register char *string, *separators; + int quoted; +{ + WORD_LIST *result; + WORD_DESC *t; + char *current_word, *s; + int sindex, sh_style_split, whitesep, xflags; + size_t slen; + + if (!string || !*string) + return ((WORD_LIST *)NULL); + + sh_style_split = separators && separators[0] == ' ' && + separators[1] == '\t' && + separators[2] == '\n' && + separators[3] == '\0'; + for (xflags = 0, s = ifs_value; s && *s; s++) + { + if (*s == CTLESC) xflags |= SX_NOCTLESC; + else if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL; + } + + slen = 0; + /* Remove sequences of whitespace at the beginning of STRING, as + long as those characters appear in IFS. Do not do this if + STRING is quoted or if there are no separator characters. */ + if (!quoted || !separators || !*separators) + { + for (s = string; *s && spctabnl (*s) && issep (*s); s++); + + if (!*s) + return ((WORD_LIST *)NULL); + + string = s; + } + + /* OK, now STRING points to a word that does not begin with white space. + The splitting algorithm is: + extract a word, stopping at a separator + skip sequences of spc, tab, or nl as long as they are separators + This obeys the field splitting rules in Posix.2. */ + slen = (MB_CUR_MAX > 1) ? strlen (string) : 1; + for (result = (WORD_LIST *)NULL, sindex = 0; string[sindex]; ) + { + /* Don't need string length in ADVANCE_CHAR or string_extract_verbatim + unless multibyte chars are possible. */ + current_word = string_extract_verbatim (string, slen, &sindex, separators, xflags); + if (current_word == 0) + break; + + /* If we have a quoted empty string, add a quoted null argument. We + want to preserve the quoted null character iff this is a quoted + empty string; otherwise the quoted null characters are removed + below. */ + if (QUOTED_NULL (current_word)) + { + t = alloc_word_desc (); + t->word = make_quoted_char ('\0'); + t->flags |= W_QUOTED|W_HASQUOTEDNULL; + result = make_word_list (t, result); + } + else if (current_word[0] != '\0') + { + /* If we have something, then add it regardless. However, + perform quoted null character removal on the current word. */ + remove_quoted_nulls (current_word); + result = add_string_to_list (current_word, result); + result->word->flags &= ~W_HASQUOTEDNULL; /* just to be sure */ + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + result->word->flags |= W_QUOTED; + } + + /* If we're not doing sequences of separators in the traditional + Bourne shell style, then add a quoted null argument. */ + else if (!sh_style_split && !spctabnl (string[sindex])) + { + t = alloc_word_desc (); + t->word = make_quoted_char ('\0'); + t->flags |= W_QUOTED|W_HASQUOTEDNULL; + result = make_word_list (t, result); + } + + free (current_word); + + /* Note whether or not the separator is IFS whitespace, used later. */ + whitesep = string[sindex] && spctabnl (string[sindex]); + + /* Move past the current separator character. */ + if (string[sindex]) + { + DECLARE_MBSTATE; + ADVANCE_CHAR (string, slen, sindex); + } + + /* Now skip sequences of space, tab, or newline characters if they are + in the list of separators. */ + while (string[sindex] && spctabnl (string[sindex]) && issep (string[sindex])) + sindex++; + + /* If the first separator was IFS whitespace and the current character + is a non-whitespace IFS character, it should be part of the current + field delimiter, not a separate delimiter that would result in an + empty field. Look at POSIX.2, 3.6.5, (3)(b). */ + if (string[sindex] && whitesep && issep (string[sindex]) && !spctabnl (string[sindex])) + { + sindex++; + /* An IFS character that is not IFS white space, along with any + adjacent IFS white space, shall delimit a field. (SUSv3) */ + while (string[sindex] && spctabnl (string[sindex]) && isifs (string[sindex])) + sindex++; + } + } + return (REVERSE_LIST (result, WORD_LIST *)); +} + +/* Parse a single word from STRING, using SEPARATORS to separate fields. + ENDPTR is set to the first character after the word. This is used by + the `read' builtin. This is never called with SEPARATORS != $IFS; + it should be simplified. + + XXX - this function is very similar to list_string; they should be + combined - XXX */ +char * +get_word_from_string (stringp, separators, endptr) + char **stringp, *separators, **endptr; +{ + register char *s; + char *current_word; + int sindex, sh_style_split, whitesep, xflags; + size_t slen; + + if (!stringp || !*stringp || !**stringp) + return ((char *)NULL); + + sh_style_split = separators && separators[0] == ' ' && + separators[1] == '\t' && + separators[2] == '\n' && + separators[3] == '\0'; + for (xflags = 0, s = ifs_value; s && *s; s++) + { + if (*s == CTLESC) xflags |= SX_NOCTLESC; + if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL; + } + + s = *stringp; + slen = 0; + + /* Remove sequences of whitespace at the beginning of STRING, as + long as those characters appear in IFS. */ + if (sh_style_split || !separators || !*separators) + { + for (; *s && spctabnl (*s) && isifs (*s); s++); + + /* If the string is nothing but whitespace, update it and return. */ + if (!*s) + { + *stringp = s; + if (endptr) + *endptr = s; + return ((char *)NULL); + } + } + + /* OK, S points to a word that does not begin with white space. + Now extract a word, stopping at a separator, save a pointer to + the first character after the word, then skip sequences of spc, + tab, or nl as long as they are separators. + + This obeys the field splitting rules in Posix.2. */ + sindex = 0; + /* Don't need string length in ADVANCE_CHAR or string_extract_verbatim + unless multibyte chars are possible. */ + slen = (MB_CUR_MAX > 1) ? strlen (s) : 1; + current_word = string_extract_verbatim (s, slen, &sindex, separators, xflags); + + /* Set ENDPTR to the first character after the end of the word. */ + if (endptr) + *endptr = s + sindex; + + /* Note whether or not the separator is IFS whitespace, used later. */ + whitesep = s[sindex] && spctabnl (s[sindex]); + + /* Move past the current separator character. */ + if (s[sindex]) + { + DECLARE_MBSTATE; + ADVANCE_CHAR (s, slen, sindex); + } + + /* Now skip sequences of space, tab, or newline characters if they are + in the list of separators. */ + while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex])) + sindex++; + + /* If the first separator was IFS whitespace and the current character is + a non-whitespace IFS character, it should be part of the current field + delimiter, not a separate delimiter that would result in an empty field. + Look at POSIX.2, 3.6.5, (3)(b). */ + if (s[sindex] && whitesep && isifs (s[sindex]) && !spctabnl (s[sindex])) + { + sindex++; + /* An IFS character that is not IFS white space, along with any adjacent + IFS white space, shall delimit a field. */ + while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex])) + sindex++; + } + + /* Update STRING to point to the next field. */ + *stringp = s + sindex; + return (current_word); +} + +/* Remove IFS white space at the end of STRING. Start at the end + of the string and walk backwards until the beginning of the string + or we find a character that's not IFS white space and not CTLESC. + Only let CTLESC escape a white space character if SAW_ESCAPE is + non-zero. */ +char * +strip_trailing_ifs_whitespace (string, separators, saw_escape) + char *string, *separators; + int saw_escape; +{ + char *s; + + s = string + STRLEN (string) - 1; + while (s > string && ((spctabnl (*s) && isifs (*s)) || + (saw_escape && *s == CTLESC && spctabnl (s[1])))) + s--; + *++s = '\0'; + return string; +} + +#if 0 +/* UNUSED */ +/* Split STRING into words at whitespace. Obeys shell-style quoting with + backslashes, single and double quotes. */ +WORD_LIST * +list_string_with_quotes (string) + char *string; +{ + WORD_LIST *list; + char *token, *s; + size_t s_len; + int c, i, tokstart, len; + + for (s = string; s && *s && spctabnl (*s); s++) + ; + if (s == 0 || *s == 0) + return ((WORD_LIST *)NULL); + + s_len = strlen (s); + tokstart = i = 0; + list = (WORD_LIST *)NULL; + while (1) + { + c = s[i]; + if (c == '\\') + { + i++; + if (s[i]) + i++; + } + else if (c == '\'') + i = skip_single_quoted (s, s_len, ++i); + else if (c == '"') + i = skip_double_quoted (s, s_len, ++i); + else if (c == 0 || spctabnl (c)) + { + /* We have found the end of a token. Make a word out of it and + add it to the word list. */ + token = substring (s, tokstart, i); + list = add_string_to_list (token, list); + free (token); + while (spctabnl (s[i])) + i++; + if (s[i]) + tokstart = i; + else + break; + } + else + i++; /* normal character */ + } + return (REVERSE_LIST (list, WORD_LIST *)); +} +#endif + +/********************************************************/ +/* */ +/* Functions to perform assignment statements */ +/* */ +/********************************************************/ + +#if defined (ARRAY_VARS) +static SHELL_VAR * +do_compound_assignment (name, value, flags) + char *name, *value; + int flags; +{ + SHELL_VAR *v; + int mklocal, mkassoc; + WORD_LIST *list; + + mklocal = flags & ASS_MKLOCAL; + mkassoc = flags & ASS_MKASSOC; + + if (mklocal && variable_context) + { + v = find_variable (name); + list = expand_compound_array_assignment (v, value, flags); + if (mkassoc) + v = make_local_assoc_variable (name); + else if (v == 0 || (array_p (v) == 0 && assoc_p (v) == 0) || v->context != variable_context) + v = make_local_array_variable (name); + assign_compound_array_list (v, list, flags); + } + else + v = assign_array_from_string (name, value, flags); + + return (v); +} +#endif + +/* Given STRING, an assignment string, get the value of the right side + of the `=', and bind it to the left side. If EXPAND is true, then + perform parameter expansion, command substitution, and arithmetic + expansion on the right-hand side. Perform tilde expansion in any + case. Do not perform word splitting on the result of expansion. */ +static int +do_assignment_internal (word, expand) + const WORD_DESC *word; + int expand; +{ + int offset, appendop, assign_list, aflags, retval; + char *name, *value, *temp; + SHELL_VAR *entry; +#if defined (ARRAY_VARS) + char *t; + int ni; +#endif + const char *string; + + if (word == 0 || word->word == 0) + return 0; + + appendop = assign_list = aflags = 0; + string = word->word; + offset = assignment (string, 0); + name = savestring (string); + value = (char *)NULL; + + if (name[offset] == '=') + { + if (name[offset - 1] == '+') + { + appendop = 1; + name[offset - 1] = '\0'; + } + + name[offset] = 0; /* might need this set later */ + temp = name + offset + 1; + +#if defined (ARRAY_VARS) + if (expand && (word->flags & W_COMPASSIGN)) + { + assign_list = ni = 1; + value = extract_array_assignment_list (temp, &ni); + } + else +#endif + if (expand && temp[0]) + value = expand_string_if_necessary (temp, 0, expand_string_assignment); + else + value = savestring (temp); + } + + if (value == 0) + { + value = (char *)xmalloc (1); + value[0] = '\0'; + } + + if (echo_command_at_execute) + { + if (appendop) + name[offset - 1] = '+'; + xtrace_print_assignment (name, value, assign_list, 1); + if (appendop) + name[offset - 1] = '\0'; + } + +#define ASSIGN_RETURN(r) do { FREE (value); free (name); return (r); } while (0) + + if (appendop) + aflags |= ASS_APPEND; + +#if defined (ARRAY_VARS) + if (t = mbschr (name, '[')) /*]*/ + { + if (assign_list) + { + report_error (_("%s: cannot assign list to array member"), name); + ASSIGN_RETURN (0); + } + entry = assign_array_element (name, value, aflags); + if (entry == 0) + ASSIGN_RETURN (0); + } + else if (assign_list) + { + if (word->flags & W_ASSIGNARG) + aflags |= ASS_MKLOCAL; + if (word->flags & W_ASSIGNASSOC) + aflags |= ASS_MKASSOC; + entry = do_compound_assignment (name, value, aflags); + } + else +#endif /* ARRAY_VARS */ + entry = bind_variable (name, value, aflags); + + stupidly_hack_special_variables (name); + +#if 1 + /* Return 1 if the assignment seems to have been performed correctly. */ + if (entry == 0 || readonly_p (entry)) + retval = 0; /* assignment failure */ + else if (noassign_p (entry)) + { + last_command_exit_value = EXECUTION_FAILURE; + retval = 1; /* error status, but not assignment failure */ + } + else + retval = 1; + + if (entry && retval != 0 && noassign_p (entry) == 0) + VUNSETATTR (entry, att_invisible); + + ASSIGN_RETURN (retval); +#else + if (entry) + VUNSETATTR (entry, att_invisible); + + ASSIGN_RETURN (entry ? ((readonly_p (entry) == 0) && noassign_p (entry) == 0) : 0); +#endif +} + +/* Perform the assignment statement in STRING, and expand the + right side by doing tilde, command and parameter expansion. */ +int +do_assignment (string) + char *string; +{ + WORD_DESC td; + + td.flags = W_ASSIGNMENT; + td.word = string; + + return do_assignment_internal (&td, 1); +} + +int +do_word_assignment (word) + WORD_DESC *word; +{ + return do_assignment_internal (word, 1); +} + +/* Given STRING, an assignment string, get the value of the right side + of the `=', and bind it to the left side. Do not perform any word + expansions on the right hand side. */ +int +do_assignment_no_expand (string) + char *string; +{ + WORD_DESC td; + + td.flags = W_ASSIGNMENT; + td.word = string; + + return (do_assignment_internal (&td, 0)); +} + +/*************************************************** + * * + * Functions to manage the positional parameters * + * * + ***************************************************/ + +/* Return the word list that corresponds to `$*'. */ +WORD_LIST * +list_rest_of_args () +{ + register WORD_LIST *list, *args; + int i; + + /* Break out of the loop as soon as one of the dollar variables is null. */ + for (i = 1, list = (WORD_LIST *)NULL; i < 10 && dollar_vars[i]; i++) + list = make_word_list (make_bare_word (dollar_vars[i]), list); + + for (args = rest_of_args; args; args = args->next) + list = make_word_list (make_bare_word (args->word->word), list); + + return (REVERSE_LIST (list, WORD_LIST *)); +} + +int +number_of_args () +{ + register WORD_LIST *list; + int n; + + for (n = 0; n < 9 && dollar_vars[n+1]; n++) + ; + for (list = rest_of_args; list; list = list->next) + n++; + return n; +} + +/* Return the value of a positional parameter. This handles values > 10. */ +char * +get_dollar_var_value (ind) + intmax_t ind; +{ + char *temp; + WORD_LIST *p; + + if (ind < 10) + temp = dollar_vars[ind] ? savestring (dollar_vars[ind]) : (char *)NULL; + else /* We want something like ${11} */ + { + ind -= 10; + for (p = rest_of_args; p && ind--; p = p->next) + ; + temp = p ? savestring (p->word->word) : (char *)NULL; + } + return (temp); +} + +/* Make a single large string out of the dollar digit variables, + and the rest_of_args. If DOLLAR_STAR is 1, then obey the special + case of "$*" with respect to IFS. */ +char * +string_rest_of_args (dollar_star) + int dollar_star; +{ + register WORD_LIST *list; + char *string; + + list = list_rest_of_args (); + string = dollar_star ? string_list_dollar_star (list) : string_list (list); + dispose_words (list); + return (string); +} + +/* Return a string containing the positional parameters from START to + END, inclusive. If STRING[0] == '*', we obey the rules for $*, + which only makes a difference if QUOTED is non-zero. If QUOTED includes + Q_HERE_DOCUMENT or Q_DOUBLE_QUOTES, this returns a quoted list, otherwise + no quoting chars are added. */ +static char * +pos_params (string, start, end, quoted) + char *string; + int start, end, quoted; +{ + WORD_LIST *save, *params, *h, *t; + char *ret; + int i; + + /* see if we can short-circuit. if start == end, we want 0 parameters. */ + if (start == end) + return ((char *)NULL); + + save = params = list_rest_of_args (); + if (save == 0) + return ((char *)NULL); + + if (start == 0) /* handle ${@:0[:x]} specially */ + { + t = make_word_list (make_word (dollar_vars[0]), params); + save = params = t; + } + + for (i = start ? 1 : 0; params && i < start; i++) + params = params->next; + if (params == 0) + return ((char *)NULL); + for (h = t = params; params && i < end; i++) + { + t = params; + params = params->next; + } + + t->next = (WORD_LIST *)NULL; + + ret = string_list_pos_params (string[0], h, quoted); + + if (t != params) + t->next = params; + + dispose_words (save); + return (ret); +} + +/******************************************************************/ +/* */ +/* Functions to expand strings to strings or WORD_LISTs */ +/* */ +/******************************************************************/ + +#if defined (PROCESS_SUBSTITUTION) +#define EXP_CHAR(s) (s == '$' || s == '`' || s == '<' || s == '>' || s == CTLESC || s == '~') +#else +#define EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC || s == '~') +#endif + +/* If there are any characters in STRING that require full expansion, + then call FUNC to expand STRING; otherwise just perform quote + removal if necessary. This returns a new string. */ +static char * +expand_string_if_necessary (string, quoted, func) + char *string; + int quoted; + EXPFUNC *func; +{ + WORD_LIST *list; + size_t slen; + int i, saw_quote; + char *ret; + DECLARE_MBSTATE; + + /* Don't need string length for ADVANCE_CHAR unless multibyte chars possible. */ + slen = (MB_CUR_MAX > 1) ? strlen (string) : 0; + i = saw_quote = 0; + while (string[i]) + { + if (EXP_CHAR (string[i])) + break; + else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"') + saw_quote = 1; + ADVANCE_CHAR (string, slen, i); + } + + if (string[i]) + { + list = (*func) (string, quoted); + if (list) + { + ret = string_list (list); + dispose_words (list); + } + else + ret = (char *)NULL; + } + else if (saw_quote && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)) + ret = string_quote_removal (string, quoted); + else + ret = savestring (string); + + return ret; +} + +static inline char * +expand_string_to_string_internal (string, quoted, func) + char *string; + int quoted; + EXPFUNC *func; +{ + WORD_LIST *list; + char *ret; + + if (string == 0 || *string == '\0') + return ((char *)NULL); + + list = (*func) (string, quoted); + if (list) + { + ret = string_list (list); + dispose_words (list); + } + else + ret = (char *)NULL; + + return (ret); +} + +char * +expand_string_to_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_to_string_internal (string, quoted, expand_string)); +} + +char * +expand_string_unsplit_to_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_to_string_internal (string, quoted, expand_string_unsplit)); +} + +char * +expand_assignment_string_to_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_to_string_internal (string, quoted, expand_string_assignment)); +} + +char * +expand_arith_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_if_necessary (string, quoted, expand_string)); +} + +#if defined (COND_COMMAND) +/* Just remove backslashes in STRING. Returns a new string. */ +char * +remove_backslashes (string) + char *string; +{ + char *r, *ret, *s; + + r = ret = (char *)xmalloc (strlen (string) + 1); + for (s = string; s && *s; ) + { + if (*s == '\\') + s++; + if (*s == 0) + break; + *r++ = *s++; + } + *r = '\0'; + return ret; +} + +/* This needs better error handling. */ +/* Expand W for use as an argument to a unary or binary operator in a + [[...]] expression. If SPECIAL is 1, this is the rhs argument + to the != or == operator, and should be treated as a pattern. In + this case, we quote the string specially for the globbing code. If + SPECIAL is 2, this is an rhs argument for the =~ operator, and should + be quoted appropriately for regcomp/regexec. The caller is responsible + for removing the backslashes if the unquoted word is needed later. */ +char * +cond_expand_word (w, special) + WORD_DESC *w; + int special; +{ + char *r, *p; + WORD_LIST *l; + int qflags; + + if (w->word == 0 || w->word[0] == '\0') + return ((char *)NULL); + + w->flags |= W_NOSPLIT2; + l = call_expand_word_internal (w, 0, 0, (int *)0, (int *)0); + if (l) + { + if (special == 0) + { + dequote_list (l); + r = string_list (l); + } + else + { + qflags = QGLOB_CVTNULL; + if (special == 2) + qflags |= QGLOB_REGEXP; + p = string_list (l); + r = quote_string_for_globbing (p, qflags); + free (p); + } + dispose_words (l); + } + else + r = (char *)NULL; + + return r; +} +#endif + +/* Call expand_word_internal to expand W and handle error returns. + A convenience function for functions that don't want to handle + any errors or free any memory before aborting. */ +static WORD_LIST * +call_expand_word_internal (w, q, i, c, e) + WORD_DESC *w; + int q, i, *c, *e; +{ + WORD_LIST *result; + + result = expand_word_internal (w, q, i, c, e); + if (result == &expand_word_error || result == &expand_word_fatal) + { + /* By convention, each time this error is returned, w->word has + already been freed (it sometimes may not be in the fatal case, + but that doesn't result in a memory leak because we're going + to exit in most cases). */ + w->word = (char *)NULL; + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level ((result == &expand_word_error) ? DISCARD : FORCE_EOF); + /* NOTREACHED */ + } + else + return (result); +} + +/* Perform parameter expansion, command substitution, and arithmetic + expansion on STRING, as if it were a word. Leave the result quoted. */ +static WORD_LIST * +expand_string_internal (string, quoted) + char *string; + int quoted; +{ + WORD_DESC td; + WORD_LIST *tresult; + + if (string == 0 || *string == 0) + return ((WORD_LIST *)NULL); + + td.flags = 0; + td.word = savestring (string); + + tresult = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL); + + FREE (td.word); + return (tresult); +} + +/* Expand STRING by performing parameter expansion, command substitution, + and arithmetic expansion. Dequote the resulting WORD_LIST before + returning it, but do not perform word splitting. The call to + remove_quoted_nulls () is in here because word splitting normally + takes care of quote removal. */ +WORD_LIST * +expand_string_unsplit (string, quoted) + char *string; + int quoted; +{ + WORD_LIST *value; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + expand_no_split_dollar_star = 1; + value = expand_string_internal (string, quoted); + expand_no_split_dollar_star = 0; + + if (value) + { + if (value->word) + { + remove_quoted_nulls (value->word->word); + value->word->flags &= ~W_HASQUOTEDNULL; + } + dequote_list (value); + } + return (value); +} + +/* Expand the rhs of an assignment statement */ +WORD_LIST * +expand_string_assignment (string, quoted) + char *string; + int quoted; +{ + WORD_DESC td; + WORD_LIST *value; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + expand_no_split_dollar_star = 1; + + td.flags = W_ASSIGNRHS; + td.word = savestring (string); + value = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL); + FREE (td.word); + + expand_no_split_dollar_star = 0; + + if (value) + { + if (value->word) + { + remove_quoted_nulls (value->word->word); + value->word->flags &= ~W_HASQUOTEDNULL; + } + dequote_list (value); + } + return (value); +} + + +/* Expand one of the PS? prompt strings. This is a sort of combination of + expand_string_unsplit and expand_string_internal, but returns the + passed string when an error occurs. Might want to trap other calls + to jump_to_top_level here so we don't endlessly loop. */ +WORD_LIST * +expand_prompt_string (string, quoted, wflags) + char *string; + int quoted; + int wflags; +{ + WORD_LIST *value; + WORD_DESC td; + + if (string == 0 || *string == 0) + return ((WORD_LIST *)NULL); + + td.flags = wflags; + td.word = savestring (string); + + no_longjmp_on_fatal_error = 1; + value = expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL); + no_longjmp_on_fatal_error = 0; + + if (value == &expand_word_error || value == &expand_word_fatal) + { + value = make_word_list (make_bare_word (string), (WORD_LIST *)NULL); + return value; + } + FREE (td.word); + if (value) + { + if (value->word) + { + remove_quoted_nulls (value->word->word); + value->word->flags &= ~W_HASQUOTEDNULL; + } + dequote_list (value); + } + return (value); +} + +/* Expand STRING just as if you were expanding a word, but do not dequote + the resultant WORD_LIST. This is called only from within this file, + and is used to correctly preserve quoted characters when expanding + things like ${1+"$@"}. This does parameter expansion, command + substitution, arithmetic expansion, and word splitting. */ +static WORD_LIST * +expand_string_leave_quoted (string, quoted) + char *string; + int quoted; +{ + WORD_LIST *tlist; + WORD_LIST *tresult; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + tlist = expand_string_internal (string, quoted); + + if (tlist) + { + tresult = word_list_split (tlist); + dispose_words (tlist); + return (tresult); + } + return ((WORD_LIST *)NULL); +} + +/* This does not perform word splitting or dequote the WORD_LIST + it returns. */ +static WORD_LIST * +expand_string_for_rhs (string, quoted, dollar_at_p, has_dollar_at) + char *string; + int quoted, *dollar_at_p, *has_dollar_at; +{ + WORD_DESC td; + WORD_LIST *tresult; + + if (string == 0 || *string == '\0') + return (WORD_LIST *)NULL; + + td.flags = 0; + td.word = string; + tresult = call_expand_word_internal (&td, quoted, 1, dollar_at_p, has_dollar_at); + return (tresult); +} + +/* Expand STRING just as if you were expanding a word. This also returns + a list of words. Note that filename globbing is *NOT* done for word + or string expansion, just when the shell is expanding a command. This + does parameter expansion, command substitution, arithmetic expansion, + and word splitting. Dequote the resultant WORD_LIST before returning. */ +WORD_LIST * +expand_string (string, quoted) + char *string; + int quoted; +{ + WORD_LIST *result; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + result = expand_string_leave_quoted (string, quoted); + return (result ? dequote_list (result) : result); +} + +/*************************************************** + * * + * Functions to handle quoting chars * + * * + ***************************************************/ + +/* Conventions: + + A string with s[0] == CTLNUL && s[1] == 0 is a quoted null string. + The parser passes CTLNUL as CTLESC CTLNUL. */ + +/* Quote escape characters in string s, but no other characters. This is + used to protect CTLESC and CTLNUL in variable values from the rest of + the word expansion process after the variable is expanded (word splitting + and filename generation). If IFS is null, we quote spaces as well, just + in case we split on spaces later (in the case of unquoted $@, we will + eventually attempt to split the entire word on spaces). Corresponding + code exists in dequote_escapes. Even if we don't end up splitting on + spaces, quoting spaces is not a problem. This should never be called on + a string that is quoted with single or double quotes or part of a here + document (effectively double-quoted). */ +char * +quote_escapes (string) + char *string; +{ + register char *s, *t; + size_t slen; + char *result, *send; + int quote_spaces, skip_ctlesc, skip_ctlnul; + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + + quote_spaces = (ifs_value && *ifs_value == 0); + + for (skip_ctlesc = skip_ctlnul = 0, s = ifs_value; s && *s; s++) + skip_ctlesc |= *s == CTLESC, skip_ctlnul |= *s == CTLNUL; + + t = result = (char *)xmalloc ((slen * 2) + 1); + s = string; + + while (*s) + { + if ((skip_ctlesc == 0 && *s == CTLESC) || (skip_ctlnul == 0 && *s == CTLNUL) || (quote_spaces && *s == ' ')) + *t++ = CTLESC; + COPY_CHAR_P (t, s, send); + } + *t = '\0'; + return (result); +} + +static WORD_LIST * +list_quote_escapes (list) + WORD_LIST *list; +{ + register WORD_LIST *w; + char *t; + + for (w = list; w; w = w->next) + { + t = w->word->word; + w->word->word = quote_escapes (t); + free (t); + } + return list; +} + +/* Inverse of quote_escapes; remove CTLESC protecting CTLESC or CTLNUL. + + The parser passes us CTLESC as CTLESC CTLESC and CTLNUL as CTLESC CTLNUL. + This is necessary to make unquoted CTLESC and CTLNUL characters in the + data stream pass through properly. + + We need to remove doubled CTLESC characters inside quoted strings before + quoting the entire string, so we do not double the number of CTLESC + characters. + + Also used by parts of the pattern substitution code. */ +char * +dequote_escapes (string) + char *string; +{ + register char *s, *t, *s1; + size_t slen; + char *result, *send; + int quote_spaces; + DECLARE_MBSTATE; + + if (string == 0) + return string; + + slen = strlen (string); + send = string + slen; + + t = result = (char *)xmalloc (slen + 1); + + if (strchr (string, CTLESC) == 0) + return (strcpy (result, string)); + + quote_spaces = (ifs_value && *ifs_value == 0); + + s = string; + while (*s) + { + if (*s == CTLESC && (s[1] == CTLESC || s[1] == CTLNUL || (quote_spaces && s[1] == ' '))) + { + s++; + if (*s == '\0') + break; + } + COPY_CHAR_P (t, s, send); + } + *t = '\0'; + return result; +} + +/* Return a new string with the quoted representation of character C. + This turns "" into QUOTED_NULL, so the W_HASQUOTEDNULL flag needs to be + set in any resultant WORD_DESC where this value is the word. */ +static char * +make_quoted_char (c) + int c; +{ + char *temp; + + temp = (char *)xmalloc (3); + if (c == 0) + { + temp[0] = CTLNUL; + temp[1] = '\0'; + } + else + { + temp[0] = CTLESC; + temp[1] = c; + temp[2] = '\0'; + } + return (temp); +} + +/* Quote STRING, returning a new string. This turns "" into QUOTED_NULL, so + the W_HASQUOTEDNULL flag needs to be set in any resultant WORD_DESC where + this value is the word. */ +char * +quote_string (string) + char *string; +{ + register char *t; + size_t slen; + char *result, *send; + + if (*string == 0) + { + result = (char *)xmalloc (2); + result[0] = CTLNUL; + result[1] = '\0'; + } + else + { + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + + result = (char *)xmalloc ((slen * 2) + 1); + + for (t = result; string < send; ) + { + *t++ = CTLESC; + COPY_CHAR_P (t, string, send); + } + *t = '\0'; + } + return (result); +} + +/* De-quote quoted characters in STRING. */ +char * +dequote_string (string) + char *string; +{ + register char *s, *t; + size_t slen; + char *result, *send; + DECLARE_MBSTATE; + + slen = strlen (string); + + t = result = (char *)xmalloc (slen + 1); + + if (QUOTED_NULL (string)) + { + result[0] = '\0'; + return (result); + } + + /* If no character in the string can be quoted, don't bother examining + each character. Just return a copy of the string passed to us. */ + if (strchr (string, CTLESC) == NULL) + return (strcpy (result, string)); + + send = string + slen; + s = string; + while (*s) + { + if (*s == CTLESC) + { + s++; + if (*s == '\0') + break; + } + COPY_CHAR_P (t, s, send); + } + + *t = '\0'; + return (result); +} + +/* Quote the entire WORD_LIST list. */ +static WORD_LIST * +quote_list (list) + WORD_LIST *list; +{ + register WORD_LIST *w; + char *t; + + for (w = list; w; w = w->next) + { + t = w->word->word; + w->word->word = quote_string (t); + if (*t == 0) + w->word->flags |= W_HASQUOTEDNULL; /* XXX - turn on W_HASQUOTEDNULL here? */ + w->word->flags |= W_QUOTED; + free (t); + } + return list; +} + +/* De-quote quoted characters in each word in LIST. */ +WORD_LIST * +dequote_list (list) + WORD_LIST *list; +{ + register char *s; + register WORD_LIST *tlist; + + for (tlist = list; tlist; tlist = tlist->next) + { + s = dequote_string (tlist->word->word); + if (QUOTED_NULL (tlist->word->word)) + tlist->word->flags &= ~W_HASQUOTEDNULL; + free (tlist->word->word); + tlist->word->word = s; + } + return list; +} + +/* Remove CTLESC protecting a CTLESC or CTLNUL in place. Return the passed + string. */ +char * +remove_quoted_escapes (string) + char *string; +{ + char *t; + + if (string) + { + t = dequote_escapes (string); + strcpy (string, t); + free (t); + } + + return (string); +} + +/* Perform quoted null character removal on STRING. We don't allow any + quoted null characters in the middle or at the ends of strings because + of how expand_word_internal works. remove_quoted_nulls () turns + STRING into an empty string iff it only consists of a quoted null, + and removes all unquoted CTLNUL characters. */ +char * +remove_quoted_nulls (string) + char *string; +{ + register size_t slen; + register int i, j, prev_i; + DECLARE_MBSTATE; + + if (strchr (string, CTLNUL) == 0) /* XXX */ + return string; /* XXX */ + + slen = strlen (string); + i = j = 0; + + while (i < slen) + { + if (string[i] == CTLESC) + { + /* Old code had j++, but we cannot assume that i == j at this + point -- what if a CTLNUL has already been removed from the + string? We don't want to drop the CTLESC or recopy characters + that we've already copied down. */ + i++; string[j++] = CTLESC; + if (i == slen) + break; + } + else if (string[i] == CTLNUL) + i++; + + prev_i = i; + ADVANCE_CHAR (string, slen, i); + if (j < prev_i) + { + do string[j++] = string[prev_i++]; while (prev_i < i); + } + else + j = i; + } + string[j] = '\0'; + + return (string); +} + +/* Perform quoted null character removal on each element of LIST. + This modifies LIST. */ +void +word_list_remove_quoted_nulls (list) + WORD_LIST *list; +{ + register WORD_LIST *t; + + for (t = list; t; t = t->next) + { + remove_quoted_nulls (t->word->word); + t->word->flags &= ~W_HASQUOTEDNULL; + } +} + +/* **************************************************************** */ +/* */ +/* Functions for Matching and Removing Patterns */ +/* */ +/* **************************************************************** */ + +#if defined (HANDLE_MULTIBYTE) +#if 0 /* Currently unused */ +static unsigned char * +mb_getcharlens (string, len) + char *string; + int len; +{ + int i, offset, last; + unsigned char *ret; + char *p; + DECLARE_MBSTATE; + + i = offset = 0; + last = 0; + ret = (unsigned char *)xmalloc (len); + memset (ret, 0, len); + while (string[last]) + { + ADVANCE_CHAR (string, len, offset); + ret[last] = offset - last; + last = offset; + } + return ret; +} +#endif +#endif + +/* Remove the portion of PARAM matched by PATTERN according to OP, where OP + can have one of 4 values: + RP_LONG_LEFT remove longest matching portion at start of PARAM + RP_SHORT_LEFT remove shortest matching portion at start of PARAM + RP_LONG_RIGHT remove longest matching portion at end of PARAM + RP_SHORT_RIGHT remove shortest matching portion at end of PARAM +*/ + +#define RP_LONG_LEFT 1 +#define RP_SHORT_LEFT 2 +#define RP_LONG_RIGHT 3 +#define RP_SHORT_RIGHT 4 + +/* Returns its first argument if nothing matched; new memory otherwise */ +static char * +remove_upattern (param, pattern, op) + char *param, *pattern; + int op; +{ + register int len; + register char *end; + register char *p, *ret, c; + + len = STRLEN (param); + end = param + len; + + switch (op) + { + case RP_LONG_LEFT: /* remove longest match at start */ + for (p = end; p >= param; p--) + { + c = *p; *p = '\0'; + if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + *p = c; + return (savestring (p)); + } + *p = c; + + } + break; + + case RP_SHORT_LEFT: /* remove shortest match at start */ + for (p = param; p <= end; p++) + { + c = *p; *p = '\0'; + if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + *p = c; + return (savestring (p)); + } + *p = c; + } + break; + + case RP_LONG_RIGHT: /* remove longest match at end */ + for (p = param; p <= end; p++) + { + if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + c = *p; *p = '\0'; + ret = savestring (param); + *p = c; + return (ret); + } + } + break; + + case RP_SHORT_RIGHT: /* remove shortest match at end */ + for (p = end; p >= param; p--) + { + if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + c = *p; *p = '\0'; + ret = savestring (param); + *p = c; + return (ret); + } + } + break; + } + + return (param); /* no match, return original string */ +} + +#if defined (HANDLE_MULTIBYTE) +/* Returns its first argument if nothing matched; new memory otherwise */ +static wchar_t * +remove_wpattern (wparam, wstrlen, wpattern, op) + wchar_t *wparam; + size_t wstrlen; + wchar_t *wpattern; + int op; +{ + wchar_t wc, *ret; + int n; + + switch (op) + { + case RP_LONG_LEFT: /* remove longest match at start */ + for (n = wstrlen; n >= 0; n--) + { + wc = wparam[n]; wparam[n] = L'\0'; + if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wparam[n] = wc; + return (wcsdup (wparam + n)); + } + wparam[n] = wc; + } + break; + + case RP_SHORT_LEFT: /* remove shortest match at start */ + for (n = 0; n <= wstrlen; n++) + { + wc = wparam[n]; wparam[n] = L'\0'; + if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wparam[n] = wc; + return (wcsdup (wparam + n)); + } + wparam[n] = wc; + } + break; + + case RP_LONG_RIGHT: /* remove longest match at end */ + for (n = 0; n <= wstrlen; n++) + { + if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wc = wparam[n]; wparam[n] = L'\0'; + ret = wcsdup (wparam); + wparam[n] = wc; + return (ret); + } + } + break; + + case RP_SHORT_RIGHT: /* remove shortest match at end */ + for (n = wstrlen; n >= 0; n--) + { + if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wc = wparam[n]; wparam[n] = L'\0'; + ret = wcsdup (wparam); + wparam[n] = wc; + return (ret); + } + } + break; + } + + return (wparam); /* no match, return original string */ +} +#endif /* HANDLE_MULTIBYTE */ + +static char * +remove_pattern (param, pattern, op) + char *param, *pattern; + int op; +{ + char *xret; + + if (param == NULL) + return (param); + if (*param == '\0' || pattern == NULL || *pattern == '\0') /* minor optimization */ + return (savestring (param)); + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + { + wchar_t *ret, *oret; + size_t n; + wchar_t *wparam, *wpattern; + mbstate_t ps; + + n = xdupmbstowcs (&wpattern, NULL, pattern); + if (n == (size_t)-1) + { + xret = remove_upattern (param, pattern, op); + return ((xret == param) ? savestring (param) : xret); + } + n = xdupmbstowcs (&wparam, NULL, param); + if (n == (size_t)-1) + { + free (wpattern); + xret = remove_upattern (param, pattern, op); + return ((xret == param) ? savestring (param) : xret); + } + oret = ret = remove_wpattern (wparam, n, wpattern, op); + /* Don't bother to convert wparam back to multibyte string if nothing + matched; just return copy of original string */ + if (ret == wparam) + { + free (wparam); + free (wpattern); + return (savestring (param)); + } + + free (wparam); + free (wpattern); + + n = strlen (param); + xret = (char *)xmalloc (n + 1); + memset (&ps, '\0', sizeof (mbstate_t)); + n = wcsrtombs (xret, (const wchar_t **)&ret, n, &ps); + xret[n] = '\0'; /* just to make sure */ + free (oret); + return xret; + } + else +#endif + { + xret = remove_upattern (param, pattern, op); + return ((xret == param) ? savestring (param) : xret); + } +} + +/* Return 1 of the first character of STRING could match the first + character of pattern PAT. Used to avoid n2 calls to strmatch(). */ +static int +match_pattern_char (pat, string) + char *pat, *string; +{ + char c; + + if (*string == 0) + return (0); + + switch (c = *pat++) + { + default: + return (*string == c); + case '\\': + return (*string == *pat); + case '?': + return (*pat == LPAREN ? 1 : (*string != '\0')); + case '*': + return (1); + case '+': + case '!': + case '@': + return (*pat == LPAREN ? 1 : (*string == c)); + case '[': + return (*string != '\0'); + } +} + +/* Match PAT anywhere in STRING and return the match boundaries. + This returns 1 in case of a successful match, 0 otherwise. SP + and EP are pointers into the string where the match begins and + ends, respectively. MTYPE controls what kind of match is attempted. + MATCH_BEG and MATCH_END anchor the match at the beginning and end + of the string, respectively. The longest match is returned. */ +static int +match_upattern (string, pat, mtype, sp, ep) + char *string, *pat; + int mtype; + char **sp, **ep; +{ + int c, len; + register char *p, *p1, *npat; + char *end; + + /* If the pattern doesn't match anywhere in the string, go ahead and + short-circuit right away. A minor optimization, saves a bunch of + unnecessary calls to strmatch (up to N calls for a string of N + characters) if the match is unsuccessful. To preserve the semantics + of the substring matches below, we make sure that the pattern has + `*' as first and last character, making a new pattern if necessary. */ + /* XXX - check this later if I ever implement `**' with special meaning, + since this will potentially result in `**' at the beginning or end */ + len = STRLEN (pat); + if (pat[0] != '*' || (pat[0] == '*' && pat[1] == LPAREN && extended_glob) || pat[len - 1] != '*') + { + p = npat = (char *)xmalloc (len + 3); + p1 = pat; + if (*p1 != '*' || (*p1 == '*' && p1[1] == LPAREN && extended_glob)) + *p++ = '*'; + while (*p1) + *p++ = *p1++; + if (p1[-1] != '*' || p[-2] == '\\') + *p++ = '*'; + *p = '\0'; + } + else + npat = pat; + c = strmatch (npat, string, FNMATCH_EXTFLAG); + if (npat != pat) + free (npat); + if (c == FNM_NOMATCH) + return (0); + + len = STRLEN (string); + end = string + len; + + switch (mtype) + { + case MATCH_ANY: + for (p = string; p <= end; p++) + { + if (match_pattern_char (pat, p)) + { + for (p1 = end; p1 >= p; p1--) + { + c = *p1; *p1 = '\0'; + if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) + { + *p1 = c; + *sp = p; + *ep = p1; + return 1; + } + *p1 = c; + } + } + } + + return (0); + + case MATCH_BEG: + if (match_pattern_char (pat, string) == 0) + return (0); + + for (p = end; p >= string; p--) + { + c = *p; *p = '\0'; + if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0) + { + *p = c; + *sp = string; + *ep = p; + return 1; + } + *p = c; + } + + return (0); + + case MATCH_END: + for (p = string; p <= end; p++) + { + if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) + { + *sp = p; + *ep = end; + return 1; + } + + } + + return (0); + } + + return (0); +} + +#if defined (HANDLE_MULTIBYTE) +/* Return 1 of the first character of WSTRING could match the first + character of pattern WPAT. Wide character version. */ +static int +match_pattern_wchar (wpat, wstring) + wchar_t *wpat, *wstring; +{ + wchar_t wc; + + if (*wstring == 0) + return (0); + + switch (wc = *wpat++) + { + default: + return (*wstring == wc); + case L'\\': + return (*wstring == *wpat); + case L'?': + return (*wpat == LPAREN ? 1 : (*wstring != L'\0')); + case L'*': + return (1); + case L'+': + case L'!': + case L'@': + return (*wpat == LPAREN ? 1 : (*wstring == wc)); + case L'[': + return (*wstring != L'\0'); + } +} + +/* Match WPAT anywhere in WSTRING and return the match boundaries. + This returns 1 in case of a successful match, 0 otherwise. Wide + character version. */ +static int +match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) + wchar_t *wstring; + char **indices; + size_t wstrlen; + wchar_t *wpat; + int mtype; + char **sp, **ep; +{ + wchar_t wc, *wp, *nwpat, *wp1; + int len; +#if 0 + size_t n, n1; /* Apple's gcc seems to miscompile this badly */ +#else + int n, n1; +#endif + + /* If the pattern doesn't match anywhere in the string, go ahead and + short-circuit right away. A minor optimization, saves a bunch of + unnecessary calls to strmatch (up to N calls for a string of N + characters) if the match is unsuccessful. To preserve the semantics + of the substring matches below, we make sure that the pattern has + `*' as first and last character, making a new pattern if necessary. */ + /* XXX - check this later if I ever implement `**' with special meaning, + since this will potentially result in `**' at the beginning or end */ + len = wcslen (wpat); + if (wpat[0] != L'*' || (wpat[0] == L'*' && wpat[1] == WLPAREN && extended_glob) || wpat[len - 1] != L'*') + { + wp = nwpat = (wchar_t *)xmalloc ((len + 3) * sizeof (wchar_t)); + wp1 = wpat; + if (*wp1 != L'*' || (*wp1 == '*' && wp1[1] == WLPAREN && extended_glob)) + *wp++ = L'*'; + while (*wp1 != L'\0') + *wp++ = *wp1++; + if (wp1[-1] != L'*' || wp1[-2] == L'\\') + *wp++ = L'*'; + *wp = '\0'; + } + else + nwpat = wpat; + len = wcsmatch (nwpat, wstring, FNMATCH_EXTFLAG); + if (nwpat != wpat) + free (nwpat); + if (len == FNM_NOMATCH) + return (0); + + switch (mtype) + { + case MATCH_ANY: + for (n = 0; n <= wstrlen; n++) + { + if (match_pattern_wchar (wpat, wstring + n)) + { + for (n1 = wstrlen; n1 >= n; n1--) + { + wc = wstring[n1]; wstring[n1] = L'\0'; + if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) + { + wstring[n1] = wc; + *sp = indices[n]; + *ep = indices[n1]; + return 1; + } + wstring[n1] = wc; + } + } + } + + return (0); + + case MATCH_BEG: + if (match_pattern_wchar (wpat, wstring) == 0) + return (0); + + for (n = wstrlen; n >= 0; n--) + { + wc = wstring[n]; wstring[n] = L'\0'; + if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG) == 0) + { + wstring[n] = wc; + *sp = indices[0]; + *ep = indices[n]; + return 1; + } + wstring[n] = wc; + } + + return (0); + + case MATCH_END: + for (n = 0; n <= wstrlen; n++) + { + if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) + { + *sp = indices[n]; + *ep = indices[wstrlen]; + return 1; + } + } + + return (0); + } + + return (0); +} +#endif /* HANDLE_MULTIBYTE */ + +static int +match_pattern (string, pat, mtype, sp, ep) + char *string, *pat; + int mtype; + char **sp, **ep; +{ +#if defined (HANDLE_MULTIBYTE) + int ret; + size_t n; + wchar_t *wstring, *wpat; + char **indices; +#endif + + if (string == 0 || *string == 0 || pat == 0 || *pat == 0) + return (0); + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + { + n = xdupmbstowcs (&wpat, NULL, pat); + if (n == (size_t)-1) + return (match_upattern (string, pat, mtype, sp, ep)); + n = xdupmbstowcs (&wstring, &indices, string); + if (n == (size_t)-1) + { + free (wpat); + return (match_upattern (string, pat, mtype, sp, ep)); + } + ret = match_wpattern (wstring, indices, n, wpat, mtype, sp, ep); + + free (wpat); + free (wstring); + free (indices); + + return (ret); + } + else +#endif + return (match_upattern (string, pat, mtype, sp, ep)); +} + +static int +getpatspec (c, value) + int c; + char *value; +{ + if (c == '#') + return ((*value == '#') ? RP_LONG_LEFT : RP_SHORT_LEFT); + else /* c == '%' */ + return ((*value == '%') ? RP_LONG_RIGHT : RP_SHORT_RIGHT); +} + +/* Posix.2 says that the WORD should be run through tilde expansion, + parameter expansion, command substitution and arithmetic expansion. + This leaves the result quoted, so quote_string_for_globbing () has + to be called to fix it up for strmatch (). If QUOTED is non-zero, + it means that the entire expression was enclosed in double quotes. + This means that quoting characters in the pattern do not make any + special pattern characters quoted. For example, the `*' in the + following retains its special meaning: "${foo#'*'}". */ +static char * +getpattern (value, quoted, expandpat) + char *value; + int quoted, expandpat; +{ + char *pat, *tword; + WORD_LIST *l; +#if 0 + int i; +#endif + /* There is a problem here: how to handle single or double quotes in the + pattern string when the whole expression is between double quotes? + POSIX.2 says that enclosing double quotes do not cause the pattern to + be quoted, but does that leave us a problem with @ and array[@] and their + expansions inside a pattern? */ +#if 0 + if (expandpat && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *tword) + { + i = 0; + pat = string_extract_double_quoted (tword, &i, 1); + free (tword); + tword = pat; + } +#endif + + /* expand_string_for_rhs () leaves WORD quoted and does not perform + word splitting. */ + l = *value ? expand_string_for_rhs (value, + (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? Q_PATQUOTE : quoted, + (int *)NULL, (int *)NULL) + : (WORD_LIST *)0; + pat = string_list (l); + dispose_words (l); + if (pat) + { + tword = quote_string_for_globbing (pat, QGLOB_CVTNULL); + free (pat); + pat = tword; + } + return (pat); +} + +#if 0 +/* Handle removing a pattern from a string as a result of ${name%[%]value} + or ${name#[#]value}. */ +static char * +variable_remove_pattern (value, pattern, patspec, quoted) + char *value, *pattern; + int patspec, quoted; +{ + char *tword; + + tword = remove_pattern (value, pattern, patspec); + + return (tword); +} +#endif + +static char * +list_remove_pattern (list, pattern, patspec, itype, quoted) + WORD_LIST *list; + char *pattern; + int patspec, itype, quoted; +{ + WORD_LIST *new, *l; + WORD_DESC *w; + char *tword; + + for (new = (WORD_LIST *)NULL, l = list; l; l = l->next) + { + tword = remove_pattern (l->word->word, pattern, patspec); + w = alloc_word_desc (); + w->word = tword ? tword : savestring (""); + new = make_word_list (w, new); + } + + l = REVERSE_LIST (new, WORD_LIST *); + tword = string_list_pos_params (itype, l, quoted); + dispose_words (l); + + return (tword); +} + +static char * +parameter_list_remove_pattern (itype, pattern, patspec, quoted) + int itype; + char *pattern; + int patspec, quoted; +{ + char *ret; + WORD_LIST *list; + + list = list_rest_of_args (); + if (list == 0) + return ((char *)NULL); + ret = list_remove_pattern (list, pattern, patspec, itype, quoted); + dispose_words (list); + return (ret); +} + +#if defined (ARRAY_VARS) +static char * +array_remove_pattern (var, pattern, patspec, varname, quoted) + SHELL_VAR *var; + char *pattern; + int patspec; + char *varname; /* so we can figure out how it's indexed */ + int quoted; +{ + ARRAY *a; + HASH_TABLE *h; + int itype; + char *ret; + WORD_LIST *list; + SHELL_VAR *v; + + /* compute itype from varname here */ + v = array_variable_part (varname, &ret, 0); + itype = ret[0]; + + a = (v && array_p (v)) ? array_cell (v) : 0; + h = (v && assoc_p (v)) ? assoc_cell (v) : 0; + + list = a ? array_to_word_list (a) : (h ? assoc_to_word_list (h) : 0); + if (list == 0) + return ((char *)NULL); + ret = list_remove_pattern (list, pattern, patspec, itype, quoted); + dispose_words (list); + + return ret; +} +#endif /* ARRAY_VARS */ + +static char * +parameter_brace_remove_pattern (varname, value, ind, patstr, rtype, quoted, flags) + char *varname, *value; + int ind; + char *patstr; + int rtype, quoted, flags; +{ + int vtype, patspec, starsub; + char *temp1, *val, *pattern; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + return ((char *)NULL); + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + patspec = getpatspec (rtype, patstr); + if (patspec == RP_LONG_LEFT || patspec == RP_LONG_RIGHT) + patstr++; + + /* Need to pass getpattern newly-allocated memory in case of expansion -- + the expansion code will free the passed string on an error. */ + temp1 = savestring (patstr); + pattern = getpattern (temp1, quoted, 1); + free (temp1); + + temp1 = (char *)NULL; /* shut up gcc */ + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + temp1 = remove_pattern (val, pattern, patspec); + if (vtype == VT_VARIABLE) + FREE (val); + if (temp1) + { + val = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + ? quote_string (temp1) + : quote_escapes (temp1); + free (temp1); + temp1 = val; + } + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + temp1 = array_remove_pattern (v, pattern, patspec, varname, quoted); + if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)) + { + val = quote_escapes (temp1); + free (temp1); + temp1 = val; + } + break; +#endif + case VT_POSPARMS: + temp1 = parameter_list_remove_pattern (varname[0], pattern, patspec, quoted); + if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)) + { + val = quote_escapes (temp1); + free (temp1); + temp1 = val; + } + break; + } + + FREE (pattern); + return temp1; +} + +/******************************************* + * * + * Functions to expand WORD_DESCs * + * * + *******************************************/ + +/* Expand WORD, performing word splitting on the result. This does + parameter expansion, command substitution, arithmetic expansion, + word splitting, and quote removal. */ + +WORD_LIST * +expand_word (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_LIST *result, *tresult; + + tresult = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL); + result = word_list_split (tresult); + dispose_words (tresult); + return (result ? dequote_list (result) : result); +} + +/* Expand WORD, but do not perform word splitting on the result. This + does parameter expansion, command substitution, arithmetic expansion, + and quote removal. */ +WORD_LIST * +expand_word_unsplit (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_LIST *result; + + expand_no_split_dollar_star = 1; +#if defined (HANDLE_MULTIBYTE) + if (ifs_firstc[0] == 0) +#else + if (ifs_firstc == 0) +#endif + word->flags |= W_NOSPLIT; + result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL); + expand_no_split_dollar_star = 0; + + return (result ? dequote_list (result) : result); +} + +/* Perform shell expansions on WORD, but do not perform word splitting or + quote removal on the result. Virtually identical to expand_word_unsplit; + could be combined if implementations don't diverge. */ +WORD_LIST * +expand_word_leave_quoted (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_LIST *result; + + expand_no_split_dollar_star = 1; +#if defined (HANDLE_MULTIBYTE) + if (ifs_firstc[0] == 0) +#else + if (ifs_firstc == 0) +#endif + word->flags |= W_NOSPLIT; + word->flags |= W_NOSPLIT2; + result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL); + expand_no_split_dollar_star = 0; + + return result; +} + +#if defined (PROCESS_SUBSTITUTION) + +/*****************************************************************/ +/* */ +/* Hacking Process Substitution */ +/* */ +/*****************************************************************/ + +#if !defined (HAVE_DEV_FD) +/* Named pipes must be removed explicitly with `unlink'. This keeps a list + of FIFOs the shell has open. unlink_fifo_list will walk the list and + unlink all of them. add_fifo_list adds the name of an open FIFO to the + list. NFIFO is a count of the number of FIFOs in the list. */ +#define FIFO_INCR 20 + +struct temp_fifo { + char *file; + pid_t proc; +}; + +static struct temp_fifo *fifo_list = (struct temp_fifo *)NULL; +static int nfifo; +static int fifo_list_size; + +char * +copy_fifo_list (sizep) + int *sizep; +{ + if (sizep) + *sizep = 0; + return (char *)NULL; +} + +static void +add_fifo_list (pathname) + char *pathname; +{ + if (nfifo >= fifo_list_size - 1) + { + fifo_list_size += FIFO_INCR; + fifo_list = (struct temp_fifo *)xrealloc (fifo_list, + fifo_list_size * sizeof (struct temp_fifo)); + } + + fifo_list[nfifo].file = savestring (pathname); + nfifo++; +} + +void +unlink_fifo (i) + int i; +{ + if ((fifo_list[i].proc == -1) || (kill(fifo_list[i].proc, 0) == -1)) + { + unlink (fifo_list[i].file); + free (fifo_list[i].file); + fifo_list[i].file = (char *)NULL; + fifo_list[i].proc = -1; + } +} + +void +unlink_fifo_list () +{ + int saved, i, j; + + if (nfifo == 0) + return; + + for (i = saved = 0; i < nfifo; i++) + { + if ((fifo_list[i].proc == -1) || (kill(fifo_list[i].proc, 0) == -1)) + { + unlink (fifo_list[i].file); + free (fifo_list[i].file); + fifo_list[i].file = (char *)NULL; + fifo_list[i].proc = -1; + } + else + saved++; + } + + /* If we didn't remove some of the FIFOs, compact the list. */ + if (saved) + { + for (i = j = 0; i < nfifo; i++) + if (fifo_list[i].file) + { + fifo_list[j].file = fifo_list[i].file; + fifo_list[j].proc = fifo_list[i].proc; + j++; + } + nfifo = j; + } + else + nfifo = 0; +} + +/* Take LIST, which is a bitmap denoting active FIFOs in fifo_list + from some point in the past, and close all open FIFOs in fifo_list + that are not marked as active in LIST. If LIST is NULL, close + everything in fifo_list. LSIZE is the number of elements in LIST, in + case it's larger than fifo_list_size (size of fifo_list). */ +void +close_new_fifos (list, lsize) + char *list; + int lsize; +{ + int i; + + if (list == 0) + { + unlink_fifo_list (); + return; + } + + for (i = 0; i < lsize; i++) + if (list[i] == 0 && i < fifo_list_size && fifo_list[i].proc != -1) + unlink_fifo (i); + + for (i = lsize; i < fifo_list_size; i++) + unlink_fifo (i); +} + +int +fifos_pending () +{ + return nfifo; +} + +int +num_fifos () +{ + return nfifo; +} + +static char * +make_named_pipe () +{ + char *tname; + + tname = sh_mktmpname ("sh-np", MT_USERANDOM|MT_USETMPDIR); + if (mkfifo (tname, 0600) < 0) + { + free (tname); + return ((char *)NULL); + } + + add_fifo_list (tname); + return (tname); +} + +#else /* HAVE_DEV_FD */ + +/* DEV_FD_LIST is a bitmap of file descriptors attached to pipes the shell + has open to children. NFDS is a count of the number of bits currently + set in DEV_FD_LIST. TOTFDS is a count of the highest possible number + of open files. */ +static char *dev_fd_list = (char *)NULL; +static int nfds; +static int totfds; /* The highest possible number of open files. */ + +char * +copy_fifo_list (sizep) + int *sizep; +{ + char *ret; + + if (nfds == 0 || totfds == 0) + { + if (sizep) + *sizep = 0; + return (char *)NULL; + } + + if (sizep) + *sizep = totfds; + ret = (char *)xmalloc (totfds); + return (memcpy (ret, dev_fd_list, totfds)); +} + +static void +add_fifo_list (fd) + int fd; +{ + if (dev_fd_list == 0 || fd >= totfds) + { + int ofds; + + ofds = totfds; + totfds = getdtablesize (); + if (totfds < 0 || totfds > 256) + totfds = 256; + if (fd >= totfds) + totfds = fd + 2; + + dev_fd_list = (char *)xrealloc (dev_fd_list, totfds); + memset (dev_fd_list + ofds, '\0', totfds - ofds); + } + + dev_fd_list[fd] = 1; + nfds++; +} + +int +fifos_pending () +{ + return 0; /* used for cleanup; not needed with /dev/fd */ +} + +int +num_fifos () +{ + return nfds; +} + +void +unlink_fifo (fd) + int fd; +{ + if (dev_fd_list[fd]) + { + close (fd); + dev_fd_list[fd] = 0; + nfds--; + } +} + +void +unlink_fifo_list () +{ + register int i; + + if (nfds == 0) + return; + + for (i = 0; nfds && i < totfds; i++) + unlink_fifo (i); + + nfds = 0; +} + +/* Take LIST, which is a snapshot copy of dev_fd_list from some point in + the past, and close all open fds in dev_fd_list that are not marked + as open in LIST. If LIST is NULL, close everything in dev_fd_list. + LSIZE is the number of elements in LIST, in case it's larger than + totfds (size of dev_fd_list). */ +void +close_new_fifos (list, lsize) + char *list; + int lsize; +{ + int i; + + if (list == 0) + { + unlink_fifo_list (); + return; + } + + for (i = 0; i < lsize; i++) + if (list[i] == 0 && i < totfds && dev_fd_list[i]) + unlink_fifo (i); + + for (i = lsize; i < totfds; i++) + unlink_fifo (i); +} + +#if defined (NOTDEF) +print_dev_fd_list () +{ + register int i; + + fprintf (stderr, "pid %ld: dev_fd_list:", (long)getpid ()); + fflush (stderr); + + for (i = 0; i < totfds; i++) + { + if (dev_fd_list[i]) + fprintf (stderr, " %d", i); + } + fprintf (stderr, "\n"); +} +#endif /* NOTDEF */ + +static char * +make_dev_fd_filename (fd) + int fd; +{ + char *ret, intbuf[INT_STRLEN_BOUND (int) + 1], *p; + + ret = (char *)xmalloc (sizeof (DEV_FD_PREFIX) + 8); + + strcpy (ret, DEV_FD_PREFIX); + p = inttostr (fd, intbuf, sizeof (intbuf)); + strcpy (ret + sizeof (DEV_FD_PREFIX) - 1, p); + + add_fifo_list (fd); + return (ret); +} + +#endif /* HAVE_DEV_FD */ + +/* Return a filename that will open a connection to the process defined by + executing STRING. HAVE_DEV_FD, if defined, means open a pipe and return + a filename in /dev/fd corresponding to a descriptor that is one of the + ends of the pipe. If not defined, we use named pipes on systems that have + them. Systems without /dev/fd and named pipes are out of luck. + + OPEN_FOR_READ_IN_CHILD, if 1, means open the named pipe for reading or + use the read end of the pipe and dup that file descriptor to fd 0 in + the child. If OPEN_FOR_READ_IN_CHILD is 0, we open the named pipe for + writing or use the write end of the pipe in the child, and dup that + file descriptor to fd 1 in the child. The parent does the opposite. */ + +static char * +process_substitute (string, open_for_read_in_child) + char *string; + int open_for_read_in_child; +{ + char *pathname; + int fd, result; + pid_t old_pid, pid; +#if defined (HAVE_DEV_FD) + int parent_pipe_fd, child_pipe_fd; + int fildes[2]; +#endif /* HAVE_DEV_FD */ +#if defined (JOB_CONTROL) + pid_t old_pipeline_pgrp; +#endif + + if (!string || !*string || wordexp_only) + return ((char *)NULL); + +#if !defined (HAVE_DEV_FD) + pathname = make_named_pipe (); +#else /* HAVE_DEV_FD */ + if (pipe (fildes) < 0) + { + sys_error (_("cannot make pipe for process substitution")); + return ((char *)NULL); + } + /* If OPEN_FOR_READ_IN_CHILD == 1, we want to use the write end of + the pipe in the parent, otherwise the read end. */ + parent_pipe_fd = fildes[open_for_read_in_child]; + child_pipe_fd = fildes[1 - open_for_read_in_child]; + /* Move the parent end of the pipe to some high file descriptor, to + avoid clashes with FDs used by the script. */ + parent_pipe_fd = move_to_high_fd (parent_pipe_fd, 1, 64); + + pathname = make_dev_fd_filename (parent_pipe_fd); +#endif /* HAVE_DEV_FD */ + + if (pathname == 0) + { + sys_error (_("cannot make pipe for process substitution")); + return ((char *)NULL); + } + + old_pid = last_made_pid; + +#if defined (JOB_CONTROL) + old_pipeline_pgrp = pipeline_pgrp; + pipeline_pgrp = shell_pgrp; + save_pipeline (1); +#endif /* JOB_CONTROL */ + + pid = make_child ((char *)NULL, 1); + if (pid == 0) + { + reset_terminating_signals (); /* XXX */ + free_pushed_string_input (); + /* Cancel traps, in trap.c. */ + restore_original_signals (); /* XXX - what about special builtins? bash-4.2 */ + setup_async_signals (); + subshell_environment |= SUBSHELL_COMSUB|SUBSHELL_PROCSUB; + } + +#if defined (JOB_CONTROL) + set_sigchld_handler (); + stop_making_children (); + /* XXX - should we only do this in the parent? (as in command subst) */ + pipeline_pgrp = old_pipeline_pgrp; +#endif /* JOB_CONTROL */ + + if (pid < 0) + { + sys_error (_("cannot make child for process substitution")); + free (pathname); +#if defined (HAVE_DEV_FD) + close (parent_pipe_fd); + close (child_pipe_fd); +#endif /* HAVE_DEV_FD */ + return ((char *)NULL); + } + + if (pid > 0) + { +#if defined (JOB_CONTROL) + restore_pipeline (1); +#endif + +#if !defined (HAVE_DEV_FD) + fifo_list[nfifo-1].proc = pid; +#endif + + last_made_pid = old_pid; + +#if defined (JOB_CONTROL) && defined (PGRP_PIPE) + close_pgrp_pipe (); +#endif /* JOB_CONTROL && PGRP_PIPE */ + +#if defined (HAVE_DEV_FD) + close (child_pipe_fd); +#endif /* HAVE_DEV_FD */ + + return (pathname); + } + + set_sigint_handler (); + +#if defined (JOB_CONTROL) + set_job_control (0); +#endif /* JOB_CONTROL */ + +#if !defined (HAVE_DEV_FD) + /* Open the named pipe in the child. */ + fd = open (pathname, open_for_read_in_child ? O_RDONLY|O_NONBLOCK : O_WRONLY); + if (fd < 0) + { + /* Two separate strings for ease of translation. */ + if (open_for_read_in_child) + sys_error (_("cannot open named pipe %s for reading"), pathname); + else + sys_error (_("cannot open named pipe %s for writing"), pathname); + + exit (127); + } + if (open_for_read_in_child) + { + if (sh_unset_nodelay_mode (fd) < 0) + { + sys_error (_("cannot reset nodelay mode for fd %d"), fd); + exit (127); + } + } +#else /* HAVE_DEV_FD */ + fd = child_pipe_fd; +#endif /* HAVE_DEV_FD */ + + if (dup2 (fd, open_for_read_in_child ? 0 : 1) < 0) + { + sys_error (_("cannot duplicate named pipe %s as fd %d"), pathname, + open_for_read_in_child ? 0 : 1); + exit (127); + } + + if (fd != (open_for_read_in_child ? 0 : 1)) + close (fd); + + /* Need to close any files that this process has open to pipes inherited + from its parent. */ + if (current_fds_to_close) + { + close_fd_bitmap (current_fds_to_close); + current_fds_to_close = (struct fd_bitmap *)NULL; + } + +#if defined (HAVE_DEV_FD) + /* Make sure we close the parent's end of the pipe and clear the slot + in the fd list so it is not closed later, if reallocated by, for + instance, pipe(2). */ + close (parent_pipe_fd); + dev_fd_list[parent_pipe_fd] = 0; +#endif /* HAVE_DEV_FD */ + + result = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST)); + +#if !defined (HAVE_DEV_FD) + /* Make sure we close the named pipe in the child before we exit. */ + close (open_for_read_in_child ? 0 : 1); +#endif /* !HAVE_DEV_FD */ + + exit (result); + /*NOTREACHED*/ +} +#endif /* PROCESS_SUBSTITUTION */ + +/***********************************/ +/* */ +/* Command Substitution */ +/* */ +/***********************************/ + +static char * +read_comsub (fd, quoted, rflag) + int fd, quoted; + int *rflag; +{ + char *istring, buf[128], *bufp, *s; + int istring_index, istring_size, c, tflag, skip_ctlesc, skip_ctlnul; + ssize_t bufn; + + istring = (char *)NULL; + istring_index = istring_size = bufn = tflag = 0; + + for (skip_ctlesc = skip_ctlnul = 0, s = ifs_value; s && *s; s++) + skip_ctlesc |= *s == CTLESC, skip_ctlnul |= *s == CTLNUL; + +#ifdef __CYGWIN__ + setmode (fd, O_TEXT); /* we don't want CR/LF, we want Unix-style */ +#endif + + /* Read the output of the command through the pipe. This may need to be + changed to understand multibyte characters in the future. */ + while (1) + { + if (fd < 0) + break; + if (--bufn <= 0) + { + bufn = zread (fd, buf, sizeof (buf)); + if (bufn <= 0) + break; + bufp = buf; + } + c = *bufp++; + + if (c == 0) + { +#if 0 + internal_warning ("read_comsub: ignored null byte in input"); +#endif + continue; + } + + /* Add the character to ISTRING, possibly after resizing it. */ + RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, DEFAULT_ARRAY_SIZE); + + /* This is essentially quote_string inline */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) /* || c == CTLESC || c == CTLNUL */) + istring[istring_index++] = CTLESC; + /* Escape CTLESC and CTLNUL in the output to protect those characters + from the rest of the word expansions (word splitting and globbing.) + This is essentially quote_escapes inline. */ + else if (skip_ctlesc == 0 && c == CTLESC) + { + tflag |= W_HASCTLESC; + istring[istring_index++] = CTLESC; + } + else if ((skip_ctlnul == 0 && c == CTLNUL) || (c == ' ' && (ifs_value && *ifs_value == 0))) + istring[istring_index++] = CTLESC; + + istring[istring_index++] = c; + +#if 0 +#if defined (__CYGWIN__) + if (c == '\n' && istring_index > 1 && istring[istring_index - 2] == '\r') + { + istring_index--; + istring[istring_index - 1] = '\n'; + } +#endif +#endif + } + + if (istring) + istring[istring_index] = '\0'; + + /* If we read no output, just return now and save ourselves some + trouble. */ + if (istring_index == 0) + { + FREE (istring); + if (rflag) + *rflag = tflag; + return (char *)NULL; + } + + /* Strip trailing newlines from the output of the command. */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + { + while (istring_index > 0) + { + if (istring[istring_index - 1] == '\n') + { + --istring_index; + + /* If the newline was quoted, remove the quoting char. */ + if (istring[istring_index - 1] == CTLESC) + --istring_index; + } + else + break; + } + istring[istring_index] = '\0'; + } + else + strip_trailing (istring, istring_index - 1, 1); + + if (rflag) + *rflag = tflag; + return istring; +} + +/* Perform command substitution on STRING. This returns a WORD_DESC * with the + contained string possibly quoted. */ +WORD_DESC * +command_substitute (string, quoted) + char *string; + int quoted; +{ + pid_t pid, old_pid, old_pipeline_pgrp, old_async_pid; + char *istring; + int result, fildes[2], function_value, pflags, rc, tflag; + WORD_DESC *ret; + + istring = (char *)NULL; + + /* Don't fork () if there is no need to. In the case of no command to + run, just return NULL. */ + if (!string || !*string || (string[0] == '\n' && !string[1])) + return ((WORD_DESC *)NULL); + + if (wordexp_only && read_but_dont_execute) + { + last_command_exit_value = EX_WEXPCOMSUB; + jump_to_top_level (EXITPROG); + } + + /* We're making the assumption here that the command substitution will + eventually run a command from the file system. Since we'll run + maybe_make_export_env in this subshell before executing that command, + the parent shell and any other shells it starts will have to remake + the environment. If we make it before we fork, other shells won't + have to. Don't bother if we have any temporary variable assignments, + though, because the export environment will be remade after this + command completes anyway, but do it if all the words to be expanded + are variable assignments. */ + if (subst_assign_varlist == 0 || garglist == 0) + maybe_make_export_env (); /* XXX */ + + /* Flags to pass to parse_and_execute() */ + pflags = (interactive && sourcelevel == 0) ? SEVAL_RESETLINE : 0; + + /* Pipe the output of executing STRING into the current shell. */ + if (pipe (fildes) < 0) + { + sys_error (_("cannot make pipe for command substitution")); + goto error_exit; + } + + old_pid = last_made_pid; +#if defined (JOB_CONTROL) + old_pipeline_pgrp = pipeline_pgrp; + /* Don't reset the pipeline pgrp if we're already a subshell in a pipeline. */ + if ((subshell_environment & SUBSHELL_PIPE) == 0) + pipeline_pgrp = shell_pgrp; + cleanup_the_pipeline (); +#endif /* JOB_CONTROL */ + + old_async_pid = last_asynchronous_pid; + pid = make_child ((char *)NULL, subshell_environment&SUBSHELL_ASYNC); + last_asynchronous_pid = old_async_pid; + + if (pid == 0) + { + /* Reset the signal handlers in the child, but don't free the + trap strings. Set a flag noting that we have to free the + trap strings if we run trap to change a signal disposition. */ + reset_signal_handlers (); + subshell_environment |= SUBSHELL_RESETTRAP; + } + +#if defined (JOB_CONTROL) + /* XXX DO THIS ONLY IN PARENT ? XXX */ + set_sigchld_handler (); + stop_making_children (); + if (pid != 0) + pipeline_pgrp = old_pipeline_pgrp; +#else + stop_making_children (); +#endif /* JOB_CONTROL */ + + if (pid < 0) + { + sys_error (_("cannot make child for command substitution")); + error_exit: + + FREE (istring); + close (fildes[0]); + close (fildes[1]); + return ((WORD_DESC *)NULL); + } + + if (pid == 0) + { + set_sigint_handler (); /* XXX */ + + free_pushed_string_input (); + + if (dup2 (fildes[1], 1) < 0) + { + sys_error (_("command_substitute: cannot duplicate pipe as fd 1")); + exit (EXECUTION_FAILURE); + } + + /* If standard output is closed in the parent shell + (such as after `exec >&-'), file descriptor 1 will be + the lowest available file descriptor, and end up in + fildes[0]. This can happen for stdin and stderr as well, + but stdout is more important -- it will cause no output + to be generated from this command. */ + if ((fildes[1] != fileno (stdin)) && + (fildes[1] != fileno (stdout)) && + (fildes[1] != fileno (stderr))) + close (fildes[1]); + + if ((fildes[0] != fileno (stdin)) && + (fildes[0] != fileno (stdout)) && + (fildes[0] != fileno (stderr))) + close (fildes[0]); + + /* The currently executing shell is not interactive. */ + interactive = 0; + + /* This is a subshell environment. */ + subshell_environment |= SUBSHELL_COMSUB; + + /* When not in POSIX mode, command substitution does not inherit + the -e flag. */ + if (posixly_correct == 0) + exit_immediately_on_error = 0; + + remove_quoted_escapes (string); + + startup_state = 2; /* see if we can avoid a fork */ + /* Give command substitution a place to jump back to on failure, + so we don't go back up to main (). */ + result = setjmp (top_level); + + /* If we're running a command substitution inside a shell function, + trap `return' so we don't return from the function in the subshell + and go off to never-never land. */ + if (result == 0 && return_catch_flag) + function_value = setjmp (return_catch); + else + function_value = 0; + + if (result == ERREXIT) + rc = last_command_exit_value; + else if (result == EXITPROG) + rc = last_command_exit_value; + else if (result) + rc = EXECUTION_FAILURE; + else if (function_value) + rc = return_catch_value; + else + { + subshell_level++; + rc = parse_and_execute (string, "command substitution", pflags|SEVAL_NOHIST); + subshell_level--; + } + + last_command_exit_value = rc; + rc = run_exit_trap (); +#if defined (PROCESS_SUBSTITUTION) + unlink_fifo_list (); +#endif + exit (rc); + } + else + { +#if defined (JOB_CONTROL) && defined (PGRP_PIPE) + close_pgrp_pipe (); +#endif /* JOB_CONTROL && PGRP_PIPE */ + + close (fildes[1]); + + tflag = 0; + istring = read_comsub (fildes[0], quoted, &tflag); + + close (fildes[0]); + + current_command_subst_pid = pid; + last_command_exit_value = wait_for (pid); + last_command_subst_pid = pid; + last_made_pid = old_pid; + +#if defined (JOB_CONTROL) + /* If last_command_exit_value > 128, then the substituted command + was terminated by a signal. If that signal was SIGINT, then send + SIGINT to ourselves. This will break out of loops, for instance. */ + if (last_command_exit_value == (128 + SIGINT) && last_command_exit_signal == SIGINT) + kill (getpid (), SIGINT); + + /* wait_for gives the terminal back to shell_pgrp. If some other + process group should have it, give it away to that group here. + pipeline_pgrp is non-zero only while we are constructing a + pipline, so what we are concerned about is whether or not that + pipeline was started in the background. A pipeline started in + the background should never get the tty back here. */ + if (interactive && pipeline_pgrp != (pid_t)0 && (subshell_environment & SUBSHELL_ASYNC) == 0) + give_terminal_to (pipeline_pgrp, 0); +#endif /* JOB_CONTROL */ + + ret = alloc_word_desc (); + ret->word = istring; + ret->flags = tflag; + + return ret; + } +} + +/******************************************************** + * * + * Utility functions for parameter expansion * + * * + ********************************************************/ + +#if defined (ARRAY_VARS) + +static arrayind_t +array_length_reference (s) + char *s; +{ + int len; + arrayind_t ind; + char *akey; + char *t, c; + ARRAY *array; + HASH_TABLE *h; + SHELL_VAR *var; + + var = array_variable_part (s, &t, &len); + + /* If unbound variables should generate an error, report one and return + failure. */ + if ((var == 0 || (assoc_p (var) == 0 && array_p (var) == 0)) && unbound_vars_is_error) + { + c = *--t; + *t = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (s); + *t = c; + return (-1); + } + else if (var == 0) + return 0; + + /* We support a couple of expansions for variables that are not arrays. + We'll return the length of the value for v[0], and 1 for v[@] or + v[*]. Return 0 for everything else. */ + + array = array_p (var) ? array_cell (var) : (ARRAY *)NULL; + h = assoc_p (var) ? assoc_cell (var) : (HASH_TABLE *)NULL; + + if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']') + { + if (assoc_p (var)) + return (h ? assoc_num_elements (h) : 0); + else if (array_p (var)) + return (array ? array_num_elements (array) : 0); + else + return (var_isset (var) ? 1 : 0); + } + + if (assoc_p (var)) + { + t[len - 1] = '\0'; + akey = expand_assignment_string_to_string (t, 0); /* [ */ + t[len - 1] = ']'; + if (akey == 0 || *akey == 0) + { + err_badarraysub (t); + return (-1); + } + t = assoc_reference (assoc_cell (var), akey); + } + else + { + ind = array_expand_index (t, len); + if (ind < 0) + { + err_badarraysub (t); + return (-1); + } + if (array_p (var)) + t = array_reference (array, ind); + else + t = (ind == 0) ? value_cell (var) : (char *)NULL; + } + + len = MB_STRLEN (t); + return (len); +} +#endif /* ARRAY_VARS */ + +static int +valid_brace_expansion_word (name, var_is_special) + char *name; + int var_is_special; +{ + if (DIGIT (*name) && all_digits (name)) + return 1; + else if (var_is_special) + return 1; +#if defined (ARRAY_VARS) + else if (valid_array_reference (name)) + return 1; +#endif /* ARRAY_VARS */ + else if (legal_identifier (name)) + return 1; + else + return 0; +} + +static int +chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at) + char *name; + int quoted; + int *quoted_dollar_atp, *contains_dollar_at; +{ + char *temp1; + + if (name == 0) + { + if (quoted_dollar_atp) + *quoted_dollar_atp = 0; + if (contains_dollar_at) + *contains_dollar_at = 0; + return 0; + } + + /* check for $@ and $* */ + if (name[0] == '@' && name[1] == 0) + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } + else if (name[0] == '*' && name[1] == '\0' && quoted == 0) + { + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } + + /* Now check for ${array[@]} and ${array[*]} */ +#if defined (ARRAY_VARS) + else if (valid_array_reference (name)) + { + temp1 = mbschr (name, '['); + if (temp1 && temp1[1] == '@' && temp1[2] == ']') + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } /* [ */ + /* ${array[*]}, when unquoted, should be treated like ${array[@]}, + which should result in separate words even when IFS is unset. */ + if (temp1 && temp1[1] == '*' && temp1[2] == ']' && quoted == 0) + { + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } + } +#endif + return 0; +} + +/* Parameter expand NAME, and return a new string which is the expansion, + or NULL if there was no expansion. + VAR_IS_SPECIAL is non-zero if NAME is one of the special variables in + the shell, e.g., "@", "$", "*", etc. QUOTED, if non-zero, means that + NAME was found inside of a double-quoted expression. */ +static WORD_DESC * +parameter_brace_expand_word (name, var_is_special, quoted, pflags, indp) + char *name; + int var_is_special, quoted, pflags; + arrayind_t *indp; +{ + WORD_DESC *ret; + char *temp, *tt; + intmax_t arg_index; + SHELL_VAR *var; + int atype, rflags; + arrayind_t ind; + + ret = 0; + temp = 0; + rflags = 0; + + if (indp) + *indp = INTMAX_MIN; + + /* Handle multiple digit arguments, as in ${11}. */ + if (legal_number (name, &arg_index)) + { + tt = get_dollar_var_value (arg_index); + if (tt) + temp = (*tt && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (tt) + : quote_escapes (tt); + else + temp = (char *)NULL; + FREE (tt); + } + else if (var_is_special) /* ${@} */ + { + int sindex; + tt = (char *)xmalloc (2 + strlen (name)); + tt[sindex = 0] = '$'; + strcpy (tt + 1, name); + + ret = param_expand (tt, &sindex, quoted, (int *)NULL, (int *)NULL, + (int *)NULL, (int *)NULL, pflags); + free (tt); + } +#if defined (ARRAY_VARS) + else if (valid_array_reference (name)) + { + temp = array_value (name, quoted, 0, &atype, &ind); + if (atype == 0 && temp) + { + temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (temp) + : quote_escapes (temp); + rflags |= W_ARRAYIND; + if (indp) + *indp = ind; + } + else if (atype == 1 && temp && QUOTED_NULL (temp) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + rflags |= W_HASQUOTEDNULL; + } +#endif + else if (var = find_variable (name)) + { + if (var_isset (var) && invisible_p (var) == 0) + { +#if defined (ARRAY_VARS) + if (assoc_p (var)) + temp = assoc_reference (assoc_cell (var), "0"); + else if (array_p (var)) + temp = array_reference (array_cell (var), 0); + else + temp = value_cell (var); +#else + temp = value_cell (var); +#endif + + if (temp) + temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (temp) + : quote_escapes (temp); + } + else + temp = (char *)NULL; + } + else + temp = (char *)NULL; + + if (ret == 0) + { + ret = alloc_word_desc (); + ret->word = temp; + ret->flags |= rflags; + } + return ret; +} + +/* Expand an indirect reference to a variable: ${!NAME} expands to the + value of the variable whose name is the value of NAME. */ +static WORD_DESC * +parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at) + char *name; + int var_is_special, quoted; + int *quoted_dollar_atp, *contains_dollar_at; +{ + char *temp, *t; + WORD_DESC *w; + + w = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND, 0); + t = w->word; + /* Have to dequote here if necessary */ + if (t) + { + temp = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + ? dequote_string (t) + : dequote_escapes (t); + free (t); + t = temp; + } + dispose_word_desc (w); + + chk_atstar (t, quoted, quoted_dollar_atp, contains_dollar_at); + if (t == 0) + return (WORD_DESC *)NULL; + + w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0, 0); + free (t); + + return w; +} + +/* Expand the right side of a parameter expansion of the form ${NAMEcVALUE}, + depending on the value of C, the separating character. C can be one of + "-", "+", or "=". QUOTED is true if the entire brace expression occurs + between double quotes. */ +static WORD_DESC * +parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat) + char *name, *value; + int c, quoted, *qdollaratp, *hasdollarat; +{ + WORD_DESC *w; + WORD_LIST *l; + char *t, *t1, *temp; + int hasdol; + + /* If the entire expression is between double quotes, we want to treat + the value as a double-quoted string, with the exception that we strip + embedded unescaped double quotes (for sh backwards compatibility). */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *value) + { + hasdol = 0; + temp = string_extract_double_quoted (value, &hasdol, 1); + } + else + temp = value; + + w = alloc_word_desc (); + hasdol = 0; + /* XXX was 0 not quoted */ + l = *temp ? expand_string_for_rhs (temp, quoted, &hasdol, (int *)NULL) + : (WORD_LIST *)0; + if (hasdollarat) + *hasdollarat = hasdol || (l && l->next); + if (temp != value) + free (temp); + if (l) + { + /* The expansion of TEMP returned something. We need to treat things + slightly differently if HASDOL is non-zero. If we have "$@", the + individual words have already been quoted. We need to turn them + into a string with the words separated by the first character of + $IFS without any additional quoting, so string_list_dollar_at won't + do the right thing. We use string_list_dollar_star instead. */ + temp = (hasdol || l->next) ? string_list_dollar_star (l) : string_list (l); + + /* If l->next is not null, we know that TEMP contained "$@", since that + is the only expansion that creates more than one word. */ + if (qdollaratp && ((hasdol && quoted) || l->next)) + *qdollaratp = 1; + dispose_words (l); + } + else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && hasdol) + { + /* The brace expansion occurred between double quotes and there was + a $@ in TEMP. It does not matter if the $@ is quoted, as long as + it does not expand to anything. In this case, we want to return + a quoted empty string. */ + temp = make_quoted_char ('\0'); + w->flags |= W_HASQUOTEDNULL; + } + else + temp = (char *)NULL; + + if (c == '-' || c == '+') + { + w->word = temp; + return w; + } + + /* c == '=' */ + t = temp ? savestring (temp) : savestring (""); + t1 = dequote_string (t); + free (t); +#if defined (ARRAY_VARS) + if (valid_array_reference (name)) + assign_array_element (name, t1, 0); + else +#endif /* ARRAY_VARS */ + bind_variable (name, t1, 0); + + /* From Posix group discussion Feb-March 2010. Issue 7 0000221 */ + free (temp); + + w->word = t1; + return w; +} + +/* Deal with the right hand side of a ${name:?value} expansion in the case + that NAME is null or not set. If VALUE is non-null it is expanded and + used as the error message to print, otherwise a standard message is + printed. */ +static void +parameter_brace_expand_error (name, value) + char *name, *value; +{ + WORD_LIST *l; + char *temp; + + if (value && *value) + { + l = expand_string (value, 0); + temp = string_list (l); + report_error ("%s: %s", name, temp ? temp : ""); /* XXX was value not "" */ + FREE (temp); + dispose_words (l); + } + else + report_error (_("%s: parameter null or not set"), name); + + /* Free the data we have allocated during this expansion, since we + are about to longjmp out. */ + free (name); + FREE (value); +} + +/* Return 1 if NAME is something for which parameter_brace_expand_length is + OK to do. */ +static int +valid_length_expression (name) + char *name; +{ + return (name[1] == '\0' || /* ${#} */ + ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0') || /* special param */ + (DIGIT (name[1]) && all_digits (name + 1)) || /* ${#11} */ +#if defined (ARRAY_VARS) + valid_array_reference (name + 1) || /* ${#a[7]} */ +#endif + legal_identifier (name + 1)); /* ${#PS1} */ +} + +#if defined (HANDLE_MULTIBYTE) +size_t +mbstrlen (s) + const char *s; +{ + size_t clen, nc; + mbstate_t mbs, mbsbak; + + nc = 0; + memset (&mbs, 0, sizeof (mbs)); + mbsbak = mbs; + while ((clen = mbrlen(s, MB_CUR_MAX, &mbs)) != 0) + { + if (MB_INVALIDCH(clen)) + { + clen = 1; /* assume single byte */ + mbs = mbsbak; + } + + s += clen; + nc++; + mbsbak = mbs; + } + return nc; +} +#endif + + +/* Handle the parameter brace expansion that requires us to return the + length of a parameter. */ +static intmax_t +parameter_brace_expand_length (name) + char *name; +{ + char *t, *newname; + intmax_t number, arg_index; + WORD_LIST *list; +#if defined (ARRAY_VARS) + SHELL_VAR *var; +#endif + + if (name[1] == '\0') /* ${#} */ + number = number_of_args (); + else if ((name[1] == '@' || name[1] == '*') && name[2] == '\0') /* ${#@}, ${#*} */ + number = number_of_args (); + else if ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0') + { + /* Take the lengths of some of the shell's special parameters. */ + switch (name[1]) + { + case '-': + t = which_set_flags (); + break; + case '?': + t = itos (last_command_exit_value); + break; + case '$': + t = itos (dollar_dollar_pid); + break; + case '!': + if (last_asynchronous_pid == NO_PID) + t = (char *)NULL; + else + t = itos (last_asynchronous_pid); + break; + case '#': + t = itos (number_of_args ()); + break; + } + number = STRLEN (t); + FREE (t); + } +#if defined (ARRAY_VARS) + else if (valid_array_reference (name + 1)) + number = array_length_reference (name + 1); +#endif /* ARRAY_VARS */ + else + { + number = 0; + + if (legal_number (name + 1, &arg_index)) /* ${#1} */ + { + t = get_dollar_var_value (arg_index); + number = MB_STRLEN (t); + FREE (t); + } +#if defined (ARRAY_VARS) + else if ((var = find_variable (name + 1)) && (invisible_p (var) == 0) && (array_p (var) || assoc_p (var))) + { + if (assoc_p (var)) + t = assoc_reference (assoc_cell (var), "0"); + else + t = array_reference (array_cell (var), 0); + number = MB_STRLEN (t); + } +#endif + else /* ${#PS1} */ + { + newname = savestring (name); + newname[0] = '$'; + list = expand_string (newname, Q_DOUBLE_QUOTES); + t = list ? string_list (list) : (char *)NULL; + free (newname); + if (list) + dispose_words (list); + + number = MB_STRLEN (t); + FREE (t); + } + } + + return (number); +} + +/* Skip characters in SUBSTR until DELIM. SUBSTR is an arithmetic expression, + so we do some ad-hoc parsing of an arithmetic expression to find + the first DELIM, instead of using strchr(3). Two rules: + 1. If the substring contains a `(', read until closing `)'. + 2. If the substring contains a `?', read past one `:' for each `?'. +*/ + +static char * +skiparith (substr, delim) + char *substr; + int delim; +{ + size_t sublen; + int skipcol, pcount, i; + DECLARE_MBSTATE; + + sublen = strlen (substr); + i = skipcol = pcount = 0; + while (substr[i]) + { + /* Balance parens */ + if (substr[i] == LPAREN) + { + pcount++; + i++; + continue; + } + if (substr[i] == RPAREN && pcount) + { + pcount--; + i++; + continue; + } + if (pcount) + { + ADVANCE_CHAR (substr, sublen, i); + continue; + } + + /* Skip one `:' for each `?' */ + if (substr[i] == ':' && skipcol) + { + skipcol--; + i++; + continue; + } + if (substr[i] == delim) + break; + if (substr[i] == '?') + { + skipcol++; + i++; + continue; + } + ADVANCE_CHAR (substr, sublen, i); + } + + return (substr + i); +} + +/* Verify and limit the start and end of the desired substring. If + VTYPE == 0, a regular shell variable is being used; if it is 1, + then the positional parameters are being used; if it is 2, then + VALUE is really a pointer to an array variable that should be used. + Return value is 1 if both values were OK, 0 if there was a problem + with an invalid expression, or -1 if the values were out of range. */ +static int +verify_substring_values (v, value, substr, vtype, e1p, e2p) + SHELL_VAR *v; + char *value, *substr; + int vtype; + intmax_t *e1p, *e2p; +{ + char *t, *temp1, *temp2; + arrayind_t len; + int expok; +#if defined (ARRAY_VARS) + ARRAY *a; + HASH_TABLE *h; +#endif + + /* duplicate behavior of strchr(3) */ + t = skiparith (substr, ':'); + if (*t && *t == ':') + *t = '\0'; + else + t = (char *)0; + + temp1 = expand_arith_string (substr, Q_DOUBLE_QUOTES); + *e1p = evalexp (temp1, &expok); + free (temp1); + if (expok == 0) + return (0); + + len = -1; /* paranoia */ + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + len = MB_STRLEN (value); + break; + case VT_POSPARMS: + len = number_of_args () + 1; + if (*e1p == 0) + len++; /* add one arg if counting from $0 */ + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + /* For arrays, the first value deals with array indices. Negative + offsets count from one past the array's maximum index. Associative + arrays treat the number of elements as the maximum index. */ + if (assoc_p (v)) + { + h = assoc_cell (v); + len = assoc_num_elements (h) + (*e1p < 0); + } + else + { + a = (ARRAY *)value; + len = array_max_index (a) + (*e1p < 0); /* arrays index from 0 to n - 1 */ + } + break; +#endif + } + + if (len == -1) /* paranoia */ + return -1; + + if (*e1p < 0) /* negative offsets count from end */ + *e1p += len; + + if (*e1p > len || *e1p < 0) + return (-1); + +#if defined (ARRAY_VARS) + /* For arrays, the second offset deals with the number of elements. */ + if (vtype == VT_ARRAYVAR) + len = assoc_p (v) ? assoc_num_elements (h) : array_num_elements (a); +#endif + + if (t) + { + t++; + temp2 = savestring (t); + temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES); + free (temp2); + t[-1] = ':'; + *e2p = evalexp (temp1, &expok); + free (temp1); + if (expok == 0) + return (0); + if ((vtype == VT_ARRAYVAR || vtype == VT_POSPARMS) && *e2p < 0) + { + internal_error (_("%s: substring expression < 0"), t); + return (0); + } +#if defined (ARRAY_VARS) + /* In order to deal with sparse arrays, push the intelligence about how + to deal with the number of elements desired down to the array- + specific functions. */ + if (vtype != VT_ARRAYVAR) +#endif + { + if (*e2p < 0) + { + *e2p += len; + if (*e2p < 0 || *e2p < *e1p) + { + internal_error (_("%s: substring expression < 0"), t); + return (0); + } + } + else + *e2p += *e1p; /* want E2 chars starting at E1 */ + if (*e2p > len) + *e2p = len; + } + } + else + *e2p = len; + + return (1); +} + +/* Return the type of variable specified by VARNAME (simple variable, + positional param, or array variable). Also return the value specified + by VARNAME (value of a variable or a reference to an array element). + QUOTED is the standard description of quoting state, using Q_* defines. + FLAGS is currently a set of flags to pass to array_value. If IND is + non-null and not INTMAX_MIN, and FLAGS includes AV_USEIND, IND is + passed to array_value so the array index is not computed again. + If this returns VT_VARIABLE, the caller assumes that CTLESC and CTLNUL + characters in the value are quoted with CTLESC and takes appropriate + steps. For convenience, *VALP is set to the dequoted VALUE. */ +static int +get_var_and_type (varname, value, ind, quoted, flags, varp, valp) + char *varname, *value; + arrayind_t ind; + int quoted, flags; + SHELL_VAR **varp; + char **valp; +{ + int vtype; + char *temp; +#if defined (ARRAY_VARS) + SHELL_VAR *v; +#endif + arrayind_t lind; + + /* This sets vtype to VT_VARIABLE or VT_POSPARMS */ + vtype = (varname[0] == '@' || varname[0] == '*') && varname[1] == '\0'; + if (vtype == VT_POSPARMS && varname[0] == '*') + vtype |= VT_STARSUB; + *varp = (SHELL_VAR *)NULL; + +#if defined (ARRAY_VARS) + if (valid_array_reference (varname)) + { + v = array_variable_part (varname, &temp, (int *)0); + /* If we want to signal array_value to use an already-computed index, + set LIND to that index */ + lind = (ind != INTMAX_MIN && (flags & AV_USEIND)) ? ind : 0; + if (v && (array_p (v) || assoc_p (v))) + { /* [ */ + if (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']') + { + /* Callers have to differentiate betwen indexed and associative */ + vtype = VT_ARRAYVAR; + if (temp[0] == '*') + vtype |= VT_STARSUB; + *valp = array_p (v) ? (char *)array_cell (v) : (char *)assoc_cell (v); + } + else + { + vtype = VT_ARRAYMEMBER; + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); + } + *varp = v; + } + else if (v && (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']')) + { + vtype = VT_VARIABLE; + *varp = v; + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + *valp = dequote_string (value); + else + *valp = dequote_escapes (value); + } + else + { + vtype = VT_ARRAYMEMBER; + *varp = v; + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); + } + } + else if ((v = find_variable (varname)) && (invisible_p (v) == 0) && (assoc_p (v) || array_p (v))) + { + vtype = VT_ARRAYMEMBER; + *varp = v; + *valp = assoc_p (v) ? assoc_reference (assoc_cell (v), "0") : array_reference (array_cell (v), 0); + } + else +#endif + { + if (value && vtype == VT_VARIABLE) + { + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + *valp = dequote_string (value); + else + *valp = dequote_escapes (value); + } + else + *valp = value; + } + + return vtype; +} + +/******************************************************/ +/* */ +/* Functions to extract substrings of variable values */ +/* */ +/******************************************************/ + +#if defined (HANDLE_MULTIBYTE) +/* Character-oriented rather than strictly byte-oriented substrings. S and + E, rather being strict indices into STRING, indicate character (possibly + multibyte character) positions that require calculation. + Used by the ${param:offset[:length]} expansion. */ +static char * +mb_substring (string, s, e) + char *string; + int s, e; +{ + char *tt; + int start, stop, i, slen; + DECLARE_MBSTATE; + + start = 0; + /* Don't need string length in ADVANCE_CHAR unless multibyte chars possible. */ + slen = (MB_CUR_MAX > 1) ? STRLEN (string) : 0; + + i = s; + while (string[start] && i--) + ADVANCE_CHAR (string, slen, start); + stop = start; + i = e - s; + while (string[stop] && i--) + ADVANCE_CHAR (string, slen, stop); + tt = substring (string, start, stop); + return tt; +} +#endif + +/* Process a variable substring expansion: ${name:e1[:e2]}. If VARNAME + is `@', use the positional parameters; otherwise, use the value of + VARNAME. If VARNAME is an array variable, use the array elements. */ + +static char * +parameter_brace_substring (varname, value, ind, substr, quoted, flags) + char *varname, *value; + int ind; + char *substr; + int quoted, flags; +{ + intmax_t e1, e2; + int vtype, r, starsub; + char *temp, *val, *tt, *oname; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + oname = this_command_name; + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + { + this_command_name = oname; + return ((char *)NULL); + } + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + r = verify_substring_values (v, val, substr, vtype, &e1, &e2); + this_command_name = oname; + if (r <= 0) + return ((r == 0) ? &expand_param_error : (char *)NULL); + + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + tt = mb_substring (val, e1, e2); + else +#endif + tt = substring (val, e1, e2); + + if (vtype == VT_VARIABLE) + FREE (val); + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + temp = quote_string (tt); + else + temp = tt ? quote_escapes (tt) : (char *)NULL; + FREE (tt); + break; + case VT_POSPARMS: + tt = pos_params (varname, e1, e2, quoted); + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0) + { + temp = tt ? quote_escapes (tt) : (char *)NULL; + FREE (tt); + } + else + temp = tt; + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + if (assoc_p (v)) + /* we convert to list and take first e2 elements starting at e1th + element -- officially undefined for now */ + temp = assoc_subrange (assoc_cell (v), e1, e2, starsub, quoted); + else + /* We want E2 to be the number of elements desired (arrays can be sparse, + so verify_substring_values just returns the numbers specified and we + rely on array_subrange to understand how to deal with them). */ + temp = array_subrange (array_cell (v), e1, e2, starsub, quoted); + /* array_subrange now calls array_quote_escapes as appropriate, so the + caller no longer needs to. */ + break; +#endif + default: + temp = (char *)NULL; + } + + return temp; +} + +/****************************************************************/ +/* */ +/* Functions to perform pattern substitution on variable values */ +/* */ +/****************************************************************/ + +static int +shouldexp_replacement (s) + char *s; +{ + register char *p; + + for (p = s; p && *p; p++) + { + if (*p == '\\') + p++; + else if (*p == '&') + return 1; + } + return 0; +} + +char * +pat_subst (string, pat, rep, mflags) + char *string, *pat, *rep; + int mflags; +{ + char *ret, *s, *e, *str, *rstr, *mstr; + int rsize, rptr, l, replen, mtype, rxpand, rslen, mlen; + + if (string == 0) + return (savestring ("")); + + mtype = mflags & MATCH_TYPEMASK; + +#if 0 /* bash-4.2 ? */ + rxpand = (rep && *rep) ? shouldexp_replacement (rep) : 0; +#else + rxpand = 0; +#endif + + /* Special cases: + * 1. A null pattern with mtype == MATCH_BEG means to prefix STRING + * with REP and return the result. + * 2. A null pattern with mtype == MATCH_END means to append REP to + * STRING and return the result. + * These don't understand or process `&' in the replacement string. + */ + if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END)) + { + replen = STRLEN (rep); + l = STRLEN (string); + ret = (char *)xmalloc (replen + l + 2); + if (replen == 0) + strcpy (ret, string); + else if (mtype == MATCH_BEG) + { + strcpy (ret, rep); + strcpy (ret + replen, string); + } + else + { + strcpy (ret, string); + strcpy (ret + l, rep); + } + return (ret); + } + + ret = (char *)xmalloc (rsize = 64); + ret[0] = '\0'; + + for (replen = STRLEN (rep), rptr = 0, str = string;;) + { + if (match_pattern (str, pat, mtype, &s, &e) == 0) + break; + l = s - str; + + if (rxpand) + { + int x; + mlen = e - s; + mstr = xmalloc (mlen + 1); + for (x = 0; x < mlen; x++) + mstr[x] = s[x]; + mstr[mlen] = '\0'; + rstr = strcreplace (rep, '&', mstr, 0); + rslen = strlen (rstr); + } + else + { + rstr = rep; + rslen = replen; + } + + RESIZE_MALLOCED_BUFFER (ret, rptr, (l + rslen), rsize, 64); + + /* OK, now copy the leading unmatched portion of the string (from + str to s) to ret starting at rptr (the current offset). Then copy + the replacement string at ret + rptr + (s - str). Increment + rptr (if necessary) and str and go on. */ + if (l) + { + strncpy (ret + rptr, str, l); + rptr += l; + } + if (replen) + { + strncpy (ret + rptr, rstr, rslen); + rptr += rslen; + } + str = e; /* e == end of match */ + + if (rstr != rep) + free (rstr); + + if (((mflags & MATCH_GLOBREP) == 0) || mtype != MATCH_ANY) + break; + + if (s == e) + { + /* On a zero-length match, make sure we copy one character, since + we increment one character to avoid infinite recursion. */ + RESIZE_MALLOCED_BUFFER (ret, rptr, 1, rsize, 64); + ret[rptr++] = *str++; + e++; /* avoid infinite recursion on zero-length match */ + } + } + + /* Now copy the unmatched portion of the input string */ + if (str && *str) + { + RESIZE_MALLOCED_BUFFER (ret, rptr, STRLEN(str) + 1, rsize, 64); + strcpy (ret + rptr, str); + } + else + ret[rptr] = '\0'; + + return ret; +} + +/* Do pattern match and replacement on the positional parameters. */ +static char * +pos_params_pat_subst (string, pat, rep, mflags) + char *string, *pat, *rep; + int mflags; +{ + WORD_LIST *save, *params; + WORD_DESC *w; + char *ret; + int pchar, qflags; + + save = params = list_rest_of_args (); + if (save == 0) + return ((char *)NULL); + + for ( ; params; params = params->next) + { + ret = pat_subst (params->word->word, pat, rep, mflags); + w = alloc_word_desc (); + w->word = ret ? ret : savestring (""); + dispose_word (params->word); + params->word = w; + } + + pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@'; + qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0; + +#if 0 + if ((mflags & (MATCH_QUOTED|MATCH_STARSUB)) == (MATCH_QUOTED|MATCH_STARSUB)) + ret = string_list_dollar_star (quote_list (save)); + else if ((mflags & MATCH_STARSUB) == MATCH_STARSUB) + ret = string_list_dollar_star (save); + else if ((mflags & MATCH_QUOTED) == MATCH_QUOTED) + ret = string_list_dollar_at (save, qflags); + else + ret = string_list_dollar_star (save); +#else + ret = string_list_pos_params (pchar, save, qflags); +#endif + + dispose_words (save); + + return (ret); +} + +/* Perform pattern substitution on VALUE, which is the expansion of + VARNAME. PATSUB is an expression supplying the pattern to match + and the string to substitute. QUOTED is a flags word containing + the type of quoting currently in effect. */ +static char * +parameter_brace_patsub (varname, value, ind, patsub, quoted, flags) + char *varname, *value; + int ind; + char *patsub; + int quoted, flags; +{ + int vtype, mflags, starsub, delim; + char *val, *temp, *pat, *rep, *p, *lpatsub, *tt; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + return ((char *)NULL); + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + mflags = 0; + if (patsub && *patsub == '/') + { + mflags |= MATCH_GLOBREP; + patsub++; + } + + /* Malloc this because expand_string_if_necessary or one of the expansion + functions in its call chain may free it on a substitution error. */ + lpatsub = savestring (patsub); + + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + mflags |= MATCH_QUOTED; + + if (starsub) + mflags |= MATCH_STARSUB; + + /* If the pattern starts with a `/', make sure we skip over it when looking + for the replacement delimiter. */ +#if 0 + if (rep = quoted_strchr ((*patsub == '/') ? lpatsub+1 : lpatsub, '/', ST_BACKSL)) + *rep++ = '\0'; + else + rep = (char *)NULL; +#else + delim = skip_to_delim (lpatsub, ((*patsub == '/') ? 1 : 0), "/", 0); + if (lpatsub[delim] == '/') + { + lpatsub[delim] = 0; + rep = lpatsub + delim + 1; + } + else + rep = (char *)NULL; +#endif + + if (rep && *rep == '\0') + rep = (char *)NULL; + + /* Perform the same expansions on the pattern as performed by the + pattern removal expansions. */ + pat = getpattern (lpatsub, quoted, 1); + + if (rep) + { + if ((mflags & MATCH_QUOTED) == 0) + rep = expand_string_if_necessary (rep, quoted, expand_string_unsplit); + else + rep = expand_string_to_string_internal (rep, quoted, expand_string_unsplit); + } + + /* ksh93 doesn't allow the match specifier to be a part of the expanded + pattern. This is an extension. Make sure we don't anchor the pattern + at the beginning or end of the string if we're doing global replacement, + though. */ + p = pat; + if (mflags & MATCH_GLOBREP) + mflags |= MATCH_ANY; + else if (pat && pat[0] == '#') + { + mflags |= MATCH_BEG; + p++; + } + else if (pat && pat[0] == '%') + { + mflags |= MATCH_END; + p++; + } + else + mflags |= MATCH_ANY; + + /* OK, we now want to substitute REP for PAT in VAL. If + flags & MATCH_GLOBREP is non-zero, the substitution is done + everywhere, otherwise only the first occurrence of PAT is + replaced. The pattern matching code doesn't understand + CTLESC quoting CTLESC and CTLNUL so we use the dequoted variable + values passed in (VT_VARIABLE) so the pattern substitution + code works right. We need to requote special chars after + we're done for VT_VARIABLE and VT_ARRAYMEMBER, and for the + other cases if QUOTED == 0, since the posparams and arrays + indexed by * or @ do special things when QUOTED != 0. */ + + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + temp = pat_subst (val, p, rep, mflags); + if (vtype == VT_VARIABLE) + FREE (val); + if (temp) + { + tt = (mflags & MATCH_QUOTED) ? quote_string (temp) : quote_escapes (temp); + free (temp); + temp = tt; + } + break; + case VT_POSPARMS: + temp = pos_params_pat_subst (val, p, rep, mflags); + if (temp && (mflags & MATCH_QUOTED) == 0) + { + tt = quote_escapes (temp); + free (temp); + temp = tt; + } + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + temp = assoc_p (v) ? assoc_patsub (assoc_cell (v), p, rep, mflags) + : array_patsub (array_cell (v), p, rep, mflags); + /* Don't call quote_escapes anymore; array_patsub calls + array_quote_escapes as appropriate before adding the + space separators; ditto for assoc_patsub. */ + break; +#endif + } + + FREE (pat); + FREE (rep); + free (lpatsub); + + return temp; +} + +/****************************************************************/ +/* */ +/* Functions to perform case modification on variable values */ +/* */ +/****************************************************************/ + +/* Do case modification on the positional parameters. */ + +static char * +pos_params_modcase (string, pat, modop, mflags) + char *string, *pat; + int modop; + int mflags; +{ + WORD_LIST *save, *params; + WORD_DESC *w; + char *ret; + int pchar, qflags; + + save = params = list_rest_of_args (); + if (save == 0) + return ((char *)NULL); + + for ( ; params; params = params->next) + { + ret = sh_modcase (params->word->word, pat, modop); + w = alloc_word_desc (); + w->word = ret ? ret : savestring (""); + dispose_word (params->word); + params->word = w; + } + + pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@'; + qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0; + + ret = string_list_pos_params (pchar, save, qflags); + dispose_words (save); + + return (ret); +} + +/* Perform case modification on VALUE, which is the expansion of + VARNAME. MODSPEC is an expression supplying the type of modification + to perform. QUOTED is a flags word containing the type of quoting + currently in effect. */ +static char * +parameter_brace_casemod (varname, value, ind, modspec, patspec, quoted, flags) + char *varname, *value; + int ind, modspec; + char *patspec; + int quoted, flags; +{ + int vtype, starsub, modop, mflags, x; + char *val, *temp, *pat, *p, *lpat, *tt; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + return ((char *)NULL); + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + modop = 0; + mflags = 0; + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + mflags |= MATCH_QUOTED; + if (starsub) + mflags |= MATCH_STARSUB; + + p = patspec; + if (modspec == '^') + { + x = p && p[0] == modspec; + modop = x ? CASE_UPPER : CASE_UPFIRST; + p += x; + } + else if (modspec == ',') + { + x = p && p[0] == modspec; + modop = x ? CASE_LOWER : CASE_LOWFIRST; + p += x; + } + else if (modspec == '~') + { + x = p && p[0] == modspec; + modop = x ? CASE_TOGGLEALL : CASE_TOGGLE; + p += x; + } + + lpat = p ? savestring (p) : 0; + /* Perform the same expansions on the pattern as performed by the + pattern removal expansions. FOR LATER */ + pat = lpat ? getpattern (lpat, quoted, 1) : 0; + + /* OK, now we do the case modification. */ + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + temp = sh_modcase (val, pat, modop); + if (vtype == VT_VARIABLE) + FREE (val); + if (temp) + { + tt = (mflags & MATCH_QUOTED) ? quote_string (temp) : quote_escapes (temp); + free (temp); + temp = tt; + } + break; + + case VT_POSPARMS: + temp = pos_params_modcase (val, pat, modop, mflags); + if (temp && (mflags & MATCH_QUOTED) == 0) + { + tt = quote_escapes (temp); + free (temp); + temp = tt; + } + break; + +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + temp = assoc_p (v) ? assoc_modcase (assoc_cell (v), pat, modop, mflags) + : array_modcase (array_cell (v), pat, modop, mflags); + /* Don't call quote_escapes; array_modcase calls array_quote_escapes + as appropriate before adding the space separators; ditto for + assoc_modcase. */ + break; +#endif + } + + FREE (pat); + free (lpat); + + return temp; +} + +/* Check for unbalanced parens in S, which is the contents of $(( ... )). If + any occur, this must be a nested command substitution, so return 0. + Otherwise, return 1. A valid arithmetic expression must always have a + ( before a matching ), so any cases where there are more right parens + means that this must not be an arithmetic expression, though the parser + will not accept it without a balanced total number of parens. */ +static int +chk_arithsub (s, len) + const char *s; + int len; +{ + int i, count; + DECLARE_MBSTATE; + + i = count = 0; + while (i < len) + { + if (s[i] == LPAREN) + count++; + else if (s[i] == RPAREN) + { + count--; + if (count < 0) + return 0; + } + + switch (s[i]) + { + default: + ADVANCE_CHAR (s, len, i); + break; + + case '\\': + i++; + if (s[i]) + ADVANCE_CHAR (s, len, i); + break; + + case '\'': + i = skip_single_quoted (s, len, ++i); + break; + + case '"': + i = skip_double_quoted ((char *)s, len, ++i); + break; + } + } + + return (count == 0); +} + +/****************************************************************/ +/* */ +/* Functions to perform parameter expansion on a string */ +/* */ +/****************************************************************/ + +/* ${[#][!]name[[:][^[^]][,[,]]#[#]%[%]-=?+[word][:e1[:e2]]]} */ +static WORD_DESC * +parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, contains_dollar_at) + char *string; + int *indexp, quoted, *quoted_dollar_atp, *contains_dollar_at, pflags; +{ + int check_nullness, var_is_set, var_is_null, var_is_special; + int want_substring, want_indir, want_patsub, want_casemod; + char *name, *value, *temp, *temp1; + WORD_DESC *tdesc, *ret; + int t_index, sindex, c, tflag, modspec; + intmax_t number; + arrayind_t ind; + + temp = temp1 = value = (char *)NULL; + var_is_set = var_is_null = var_is_special = check_nullness = 0; + want_substring = want_indir = want_patsub = want_casemod = 0; + + sindex = *indexp; + t_index = ++sindex; + /* ${#var} doesn't have any of the other parameter expansions on it. */ + if (string[t_index] == '#' && legal_variable_starter (string[t_index+1])) /* {{ */ + name = string_extract (string, &t_index, "}", SX_VARNAME); + else +#if defined (CASEMOD_EXPANSIONS) + /* To enable case-toggling expansions using the `~' operator character + change the 1 to 0. */ +# if defined (CASEMOD_CAPCASE) + name = string_extract (string, &t_index, "#%^,~:-=?+/}", SX_VARNAME); +# else + name = string_extract (string, &t_index, "#%^,:-=?+/}", SX_VARNAME); +# endif /* CASEMOD_CAPCASE */ +#else + name = string_extract (string, &t_index, "#%:-=?+/}", SX_VARNAME); +#endif /* CASEMOD_EXPANSIONS */ + + ret = 0; + tflag = 0; + + ind = INTMAX_MIN; + + /* If the name really consists of a special variable, then make sure + that we have the entire name. We don't allow indirect references + to special variables except `#', `?', `@' and `*'. */ + if ((sindex == t_index && + (string[t_index] == '-' || + string[t_index] == '?' || + string[t_index] == '#')) || + (sindex == t_index - 1 && string[sindex] == '!' && + (string[t_index] == '#' || + string[t_index] == '?' || + string[t_index] == '@' || + string[t_index] == '*'))) + { + t_index++; + free (name); + temp1 = string_extract (string, &t_index, "#%:-=?+/}", 0); + name = (char *)xmalloc (3 + (strlen (temp1))); + *name = string[sindex]; + if (string[sindex] == '!') + { + /* indirect reference of $#, $?, $@, or $* */ + name[1] = string[sindex + 1]; + strcpy (name + 2, temp1); + } + else + strcpy (name + 1, temp1); + free (temp1); + } + sindex = t_index; + + /* Find out what character ended the variable name. Then + do the appropriate thing. */ + if (c = string[sindex]) + sindex++; + + /* If c is followed by one of the valid parameter expansion + characters, move past it as normal. If not, assume that + a substring specification is being given, and do not move + past it. */ + if (c == ':' && VALID_PARAM_EXPAND_CHAR (string[sindex])) + { + check_nullness++; + if (c = string[sindex]) + sindex++; + } + else if (c == ':' && string[sindex] != RBRACE) + want_substring = 1; + else if (c == '/' && string[sindex] != RBRACE) + want_patsub = 1; +#if defined (CASEMOD_EXPANSIONS) + else if (c == '^' || c == ',' || c == '~') + { + modspec = c; + want_casemod = 1; + } +#endif + + /* Catch the valid and invalid brace expressions that made it through the + tests above. */ + /* ${#-} is a valid expansion and means to take the length of $-. + Similarly for ${#?} and ${##}... */ + if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 && + VALID_SPECIAL_LENGTH_PARAM (c) && string[sindex] == RBRACE) + { + name = (char *)xrealloc (name, 3); + name[1] = c; + name[2] = '\0'; + c = string[sindex++]; + } + + /* ...but ${#%}, ${#:}, ${#=}, ${#+}, and ${#/} are errors. */ + if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 && + member (c, "%:=+/") && string[sindex] == RBRACE) + { + temp = (char *)NULL; + goto bad_substitution; + } + + /* Indirect expansion begins with a `!'. A valid indirect expansion is + either a variable name, one of the positional parameters or a special + variable that expands to one of the positional parameters. */ + want_indir = *name == '!' && + (legal_variable_starter ((unsigned char)name[1]) || DIGIT (name[1]) + || VALID_INDIR_PARAM (name[1])); + + /* Determine the value of this variable. */ + + /* Check for special variables, directly referenced. */ + if (SPECIAL_VAR (name, want_indir)) + var_is_special++; + + /* Check for special expansion things, like the length of a parameter */ + if (*name == '#' && name[1]) + { + /* If we are not pointing at the character just after the + closing brace, then we haven't gotten all of the name. + Since it begins with a special character, this is a bad + substitution. Also check NAME for validity before trying + to go on. */ + if (string[sindex - 1] != RBRACE || (valid_length_expression (name) == 0)) + { + temp = (char *)NULL; + goto bad_substitution; + } + + number = parameter_brace_expand_length (name); + free (name); + + *indexp = sindex; + if (number < 0) + return (&expand_wdesc_error); + else + { + ret = alloc_word_desc (); + ret->word = itos (number); + return ret; + } + } + + /* ${@} is identical to $@. */ + if (name[0] == '@' && name[1] == '\0') + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + + if (contains_dollar_at) + *contains_dollar_at = 1; + } + + /* Process ${!PREFIX*} expansion. */ + if (want_indir && string[sindex - 1] == RBRACE && + (string[sindex - 2] == '*' || string[sindex - 2] == '@') && + legal_variable_starter ((unsigned char) name[1])) + { + char **x; + WORD_LIST *xlist; + + temp1 = savestring (name + 1); + number = strlen (temp1); + temp1[number - 1] = '\0'; + x = all_variables_matching_prefix (temp1); + xlist = strvec_to_word_list (x, 0, 0); + if (string[sindex - 2] == '*') + temp = string_list_dollar_star (xlist); + else + { + temp = string_list_dollar_at (xlist, quoted); + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + } + free (x); + dispose_words (xlist); + free (temp1); + *indexp = sindex; + + ret = alloc_word_desc (); + ret->word = temp; + return ret; + } + +#if defined (ARRAY_VARS) + /* Process ${!ARRAY[@]} and ${!ARRAY[*]} expansion. */ /* [ */ + if (want_indir && string[sindex - 1] == RBRACE && + string[sindex - 2] == ']' && valid_array_reference (name+1)) + { + char *x, *x1; + + temp1 = savestring (name + 1); + x = array_variable_name (temp1, &x1, (int *)0); /* [ */ + FREE (x); + if (ALL_ELEMENT_SUB (x1[0]) && x1[1] == ']') + { + temp = array_keys (temp1, quoted); /* handles assoc vars too */ + if (x1[0] == '@') + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + } + + free (temp1); + *indexp = sindex; + + ret = alloc_word_desc (); + ret->word = temp; + return ret; + } + + free (temp1); + } +#endif /* ARRAY_VARS */ + + /* Make sure that NAME is valid before trying to go on. */ + if (valid_brace_expansion_word (want_indir ? name + 1 : name, + var_is_special) == 0) + { + temp = (char *)NULL; + goto bad_substitution; + } + + if (want_indir) + tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at); + else + tdesc = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND|(pflags&PF_NOSPLIT2), &ind); + + if (tdesc) + { + temp = tdesc->word; + tflag = tdesc->flags; + dispose_word_desc (tdesc); + } + else + temp = (char *)0; + +#if defined (ARRAY_VARS) + if (valid_array_reference (name)) + chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at); +#endif + + var_is_set = temp != (char *)0; + var_is_null = check_nullness && (var_is_set == 0 || *temp == 0); + + /* Get the rest of the stuff inside the braces. */ + if (c && c != RBRACE) + { + /* Extract the contents of the ${ ... } expansion + according to the Posix.2 rules. */ + value = extract_dollar_brace_string (string, &sindex, quoted, (c == '%' || c == '#') ? SX_POSIXEXP : 0); + if (string[sindex] == RBRACE) + sindex++; + else + goto bad_substitution; + } + else + value = (char *)NULL; + + *indexp = sindex; + + /* If this is a substring spec, process it and add the result. */ + if (want_substring) + { + temp1 = parameter_brace_substring (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + FREE (name); + FREE (value); + FREE (temp); + + if (temp1 == &expand_param_error) + return (&expand_wdesc_error); + else if (temp1 == &expand_param_fatal) + return (&expand_wdesc_fatal); + + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + } + else if (want_patsub) + { + temp1 = parameter_brace_patsub (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + FREE (name); + FREE (value); + FREE (temp); + + if (temp1 == &expand_param_error) + return (&expand_wdesc_error); + else if (temp1 == &expand_param_fatal) + return (&expand_wdesc_fatal); + + ret = alloc_word_desc (); + ret->word = temp1; + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + } +#if defined (CASEMOD_EXPANSIONS) + else if (want_casemod) + { + temp1 = parameter_brace_casemod (name, temp, ind, modspec, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + FREE (name); + FREE (value); + FREE (temp); + + if (temp1 == &expand_param_error) + return (&expand_wdesc_error); + else if (temp1 == &expand_param_fatal) + return (&expand_wdesc_fatal); + + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + } +#endif + + /* Do the right thing based on which character ended the variable name. */ + switch (c) + { + default: + case '\0': + bad_substitution: + report_error (_("%s: bad substitution"), string ? string : "??"); + FREE (value); + FREE (temp); + free (name); + return &expand_wdesc_error; + + case RBRACE: + if (var_is_set == 0 && unbound_vars_is_error && ((name[0] != '@' && name[0] != '*') || name[1])) + { + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (name); + FREE (value); + FREE (temp); + free (name); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + break; + + case '#': /* ${param#[#]pattern} */ + case '%': /* ${param%[%]pattern} */ + if (value == 0 || *value == '\0' || temp == 0 || *temp == '\0') + { + FREE (value); + break; + } + temp1 = parameter_brace_remove_pattern (name, temp, ind, value, c, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + free (temp); + free (value); + free (name); + + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + + case '-': + case '=': + case '?': + case '+': + if (var_is_set && var_is_null == 0) + { + /* If the operator is `+', we don't want the value of the named + variable for anything, just the value of the right hand side. */ + if (c == '+') + { + /* XXX -- if we're double-quoted and the named variable is "$@", + we want to turn off any special handling of "$@" -- + we're not using it, so whatever is on the rhs applies. */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 0; + if (contains_dollar_at) + *contains_dollar_at = 0; + + FREE (temp); + if (value) + { + /* From Posix discussion on austin-group list. Issue 221 + requires that backslashes escaping `}' inside + double-quoted ${...} be removed. */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + quoted |= Q_DOLBRACE; + ret = parameter_brace_expand_rhs (name, value, c, + quoted, + quoted_dollar_atp, + contains_dollar_at); + /* XXX - fix up later, esp. noting presence of + W_HASQUOTEDNULL in ret->flags */ + free (value); + } + else + temp = (char *)NULL; + } + else + { + FREE (value); + } + /* Otherwise do nothing; just use the value in TEMP. */ + } + else /* VAR not set or VAR is NULL. */ + { + FREE (temp); + temp = (char *)NULL; + if (c == '=' && var_is_special) + { + report_error (_("$%s: cannot assign in this way"), name); + free (name); + free (value); + return &expand_wdesc_error; + } + else if (c == '?') + { + parameter_brace_expand_error (name, value); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + else if (c != '+') + { + /* XXX -- if we're double-quoted and the named variable is "$@", + we want to turn off any special handling of "$@" -- + we're not using it, so whatever is on the rhs applies. */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 0; + if (contains_dollar_at) + *contains_dollar_at = 0; + + /* From Posix discussion on austin-group list. Issue 221 requires + that backslashes escaping `}' inside double-quoted ${...} be + removed. */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + quoted |= Q_DOLBRACE; + ret = parameter_brace_expand_rhs (name, value, c, quoted, + quoted_dollar_atp, + contains_dollar_at); + /* XXX - fix up later, esp. noting presence of + W_HASQUOTEDNULL in tdesc->flags */ + } + free (value); + } + + break; + } + free (name); + + if (ret == 0) + { + ret = alloc_word_desc (); + ret->flags = tflag; + ret->word = temp; + } + return (ret); +} + +/* Expand a single ${xxx} expansion. The braces are optional. When + the braces are used, parameter_brace_expand() does the work, + possibly calling param_expand recursively. */ +static WORD_DESC * +param_expand (string, sindex, quoted, expanded_something, + contains_dollar_at, quoted_dollar_at_p, had_quoted_null_p, + pflags) + char *string; + int *sindex, quoted, *expanded_something, *contains_dollar_at; + int *quoted_dollar_at_p, *had_quoted_null_p, pflags; +{ + char *temp, *temp1, uerror[3]; + int zindex, t_index, expok; + unsigned char c; + intmax_t number; + SHELL_VAR *var; + WORD_LIST *list; + WORD_DESC *tdesc, *ret; + int tflag; + + zindex = *sindex; + c = string[++zindex]; + + temp = (char *)NULL; + ret = tdesc = (WORD_DESC *)NULL; + tflag = 0; + + /* Do simple cases first. Switch on what follows '$'. */ + switch (c) + { + /* $0 .. $9? */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + temp1 = dollar_vars[TODIGIT (c)]; + if (unbound_vars_is_error && temp1 == (char *)NULL) + { + uerror[0] = '$'; + uerror[1] = c; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + if (temp1) + temp = (*temp1 && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ? quote_string (temp1) + : quote_escapes (temp1); + else + temp = (char *)NULL; + + break; + + /* $$ -- pid of the invoking shell. */ + case '$': + temp = itos (dollar_dollar_pid); + break; + + /* $# -- number of positional parameters. */ + case '#': + temp = itos (number_of_args ()); + break; + + /* $? -- return value of the last synchronous command. */ + case '?': + temp = itos (last_command_exit_value); + break; + + /* $- -- flags supplied to the shell on invocation or by `set'. */ + case '-': + temp = which_set_flags (); + break; + + /* $! -- Pid of the last asynchronous command. */ + case '!': + /* If no asynchronous pids have been created, expand to nothing. + If `set -u' has been executed, and no async processes have + been created, this is an expansion error. */ + if (last_asynchronous_pid == NO_PID) + { + if (expanded_something) + *expanded_something = 0; + temp = (char *)NULL; + if (unbound_vars_is_error) + { + uerror[0] = '$'; + uerror[1] = c; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + } + else + temp = itos (last_asynchronous_pid); + break; + + /* The only difference between this and $@ is when the arg is quoted. */ + case '*': /* `$*' */ + list = list_rest_of_args (); + +#if 0 + /* According to austin-group posix proposal by Geoff Clare in + <20090505091501.GA10097@squonk.masqnet> of 5 May 2009: + + "The shell shall write a message to standard error and + immediately exit when it tries to expand an unset parameter + other than the '@' and '*' special parameters." + */ + + if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0) + { + uerror[0] = '$'; + uerror[1] = '*'; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } +#endif + + /* If there are no command-line arguments, this should just + disappear if there are other characters in the expansion, + even if it's quoted. */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && list == 0) + temp = (char *)NULL; + else if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE)) + { + /* If we have "$*" we want to make a string of the positional + parameters, separated by the first character of $IFS, and + quote the whole string, including the separators. If IFS + is unset, the parameters are separated by ' '; if $IFS is + null, the parameters are concatenated. */ + temp = (quoted & (Q_DOUBLE_QUOTES|Q_PATQUOTE)) ? string_list_dollar_star (list) : string_list (list); + temp1 = quote_string (temp); + if (*temp == 0) + tflag |= W_HASQUOTEDNULL; + free (temp); + temp = temp1; + } + else + { + /* We check whether or not we're eventually going to split $* here, + for example when IFS is empty and we are processing the rhs of + an assignment statement. In that case, we don't separate the + arguments at all. Otherwise, if the $* is not quoted it is + identical to $@ */ +#if 1 +# if defined (HANDLE_MULTIBYTE) + if (expand_no_split_dollar_star && ifs_firstc[0] == 0) +# else + if (expand_no_split_dollar_star && ifs_firstc == 0) +# endif + temp = string_list_dollar_star (list); + else + temp = string_list_dollar_at (list, quoted); +#else + temp = string_list_dollar_at (list, quoted); +#endif + if (expand_no_split_dollar_star == 0 && contains_dollar_at) + *contains_dollar_at = 1; + } + + dispose_words (list); + break; + + /* When we have "$@" what we want is "$1" "$2" "$3" ... This + means that we have to turn quoting off after we split into + the individually quoted arguments so that the final split + on the first character of $IFS is still done. */ + case '@': /* `$@' */ + list = list_rest_of_args (); + +#if 0 + /* According to austin-group posix proposal by Geoff Clare in + <20090505091501.GA10097@squonk.masqnet> of 5 May 2009: + + "The shell shall write a message to standard error and + immediately exit when it tries to expand an unset parameter + other than the '@' and '*' special parameters." + */ + + if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0) + { + uerror[0] = '$'; + uerror[1] = '@'; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } +#endif + + /* We want to flag the fact that we saw this. We can't turn + off quoting entirely, because other characters in the + string might need it (consider "\"$@\""), but we need some + way to signal that the final split on the first character + of $IFS should be done, even though QUOTED is 1. */ + /* XXX - should this test include Q_PATQUOTE? */ + if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + *quoted_dollar_at_p = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + +#if 0 + if (pflags & PF_NOSPLIT2) + temp = string_list_internal (quoted ? quote_list (list) : list, " "); + else +#endif + /* We want to separate the positional parameters with the first + character of $IFS in case $IFS is something other than a space. + We also want to make sure that splitting is done no matter what -- + according to POSIX.2, this expands to a list of the positional + parameters no matter what IFS is set to. */ + temp = string_list_dollar_at (list, quoted); + + dispose_words (list); + break; + + case LBRACE: + tdesc = parameter_brace_expand (string, &zindex, quoted, pflags, + quoted_dollar_at_p, + contains_dollar_at); + + if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal) + return (tdesc); + temp = tdesc ? tdesc->word : (char *)0; + + /* XXX */ + /* Quoted nulls should be removed if there is anything else + in the string. */ + /* Note that we saw the quoted null so we can add one back at + the end of this function if there are no other characters + in the string, discard TEMP, and go on. The exception to + this is when we have "${@}" and $1 is '', since $@ needs + special handling. */ + if (tdesc && tdesc->word && (tdesc->flags & W_HASQUOTEDNULL) && QUOTED_NULL (temp)) + { + if (had_quoted_null_p) + *had_quoted_null_p = 1; + if (*quoted_dollar_at_p == 0) + { + free (temp); + tdesc->word = temp = (char *)NULL; + } + + } + + ret = tdesc; + goto return0; + + /* Do command or arithmetic substitution. */ + case LPAREN: + /* We have to extract the contents of this paren substitution. */ + t_index = zindex + 1; + temp = extract_command_subst (string, &t_index, 0); + zindex = t_index; + + /* For Posix.2-style `$(( ))' arithmetic substitution, + extract the expression and pass it to the evaluator. */ + if (temp && *temp == LPAREN) + { + char *temp2; + temp1 = temp + 1; + temp2 = savestring (temp1); + t_index = strlen (temp2) - 1; + + if (temp2[t_index] != RPAREN) + { + free (temp2); + goto comsub; + } + + /* Cut off ending `)' */ + temp2[t_index] = '\0'; + + if (chk_arithsub (temp2, t_index) == 0) + { + free (temp2); +#if 0 + internal_warning (_("future versions of the shell will force evaluation as an arithmetic substitution")); +#endif + goto comsub; + } + + /* Expand variables found inside the expression. */ + temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES); + free (temp2); + +arithsub: + /* No error messages. */ + this_command_name = (char *)NULL; + number = evalexp (temp1, &expok); + free (temp); + free (temp1); + if (expok == 0) + { + if (interactive_shell == 0 && posixly_correct) + { + last_command_exit_value = EXECUTION_FAILURE; + return (&expand_wdesc_fatal); + } + else + return (&expand_wdesc_error); + } + temp = itos (number); + break; + } + +comsub: + if (pflags & PF_NOCOMSUB) + /* we need zindex+1 because string[zindex] == RPAREN */ + temp1 = substring (string, *sindex, zindex+1); + else + { + tdesc = command_substitute (temp, quoted); + temp1 = tdesc ? tdesc->word : (char *)NULL; + if (tdesc) + dispose_word_desc (tdesc); + } + FREE (temp); + temp = temp1; + break; + + /* Do POSIX.2d9-style arithmetic substitution. This will probably go + away in a future bash release. */ + case '[': + /* Extract the contents of this arithmetic substitution. */ + t_index = zindex + 1; + temp = extract_arithmetic_subst (string, &t_index); + zindex = t_index; + if (temp == 0) + { + temp = savestring (string); + if (expanded_something) + *expanded_something = 0; + goto return0; + } + + /* Do initial variable expansion. */ + temp1 = expand_arith_string (temp, Q_DOUBLE_QUOTES); + + goto arithsub; + + default: + /* Find the variable in VARIABLE_LIST. */ + temp = (char *)NULL; + + for (t_index = zindex; (c = string[zindex]) && legal_variable_char (c); zindex++) + ; + temp1 = (zindex > t_index) ? substring (string, t_index, zindex) : (char *)NULL; + + /* If this isn't a variable name, then just output the `$'. */ + if (temp1 == 0 || *temp1 == '\0') + { + FREE (temp1); + temp = (char *)xmalloc (2); + temp[0] = '$'; + temp[1] = '\0'; + if (expanded_something) + *expanded_something = 0; + goto return0; + } + + /* If the variable exists, return its value cell. */ + var = find_variable (temp1); + + if (var && invisible_p (var) == 0 && var_isset (var)) + { +#if defined (ARRAY_VARS) + if (assoc_p (var) || array_p (var)) + { + temp = array_p (var) ? array_reference (array_cell (var), 0) + : assoc_reference (assoc_cell (var), "0"); + if (temp) + temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ? quote_string (temp) + : quote_escapes (temp); + else if (unbound_vars_is_error) + goto unbound_variable; + } + else +#endif + { + temp = value_cell (var); + + temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ? quote_string (temp) + : quote_escapes (temp); + } + + free (temp1); + + goto return0; + } + + temp = (char *)NULL; + +unbound_variable: + if (unbound_vars_is_error) + { + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (temp1); + } + else + { + free (temp1); + goto return0; + } + + free (temp1); + last_command_exit_value = EXECUTION_FAILURE; + return ((unbound_vars_is_error && interactive_shell == 0) + ? &expand_wdesc_fatal + : &expand_wdesc_error); + } + + if (string[zindex]) + zindex++; + +return0: + *sindex = zindex; + + if (ret == 0) + { + ret = alloc_word_desc (); + ret->flags = tflag; /* XXX */ + ret->word = temp; + } + return ret; +} + +/* Make a word list which is the result of parameter and variable + expansion, command substitution, arithmetic substitution, and + quote removal of WORD. Return a pointer to a WORD_LIST which is + the result of the expansion. If WORD contains a null word, the + word list returned is also null. + + QUOTED contains flag values defined in shell.h. + + ISEXP is used to tell expand_word_internal that the word should be + treated as the result of an expansion. This has implications for + how IFS characters in the word are treated. + + CONTAINS_DOLLAR_AT and EXPANDED_SOMETHING are return values; when non-null + they point to an integer value which receives information about expansion. + CONTAINS_DOLLAR_AT gets non-zero if WORD contained "$@", else zero. + EXPANDED_SOMETHING get non-zero if WORD contained any parameter expansions, + else zero. + + This only does word splitting in the case of $@ expansion. In that + case, we split on ' '. */ + +/* Values for the local variable quoted_state. */ +#define UNQUOTED 0 +#define PARTIALLY_QUOTED 1 +#define WHOLLY_QUOTED 2 + +static WORD_LIST * +expand_word_internal (word, quoted, isexp, contains_dollar_at, expanded_something) + WORD_DESC *word; + int quoted, isexp; + int *contains_dollar_at; + int *expanded_something; +{ + WORD_LIST *list; + WORD_DESC *tword; + + /* The intermediate string that we build while expanding. */ + char *istring; + + /* The current size of the above object. */ + int istring_size; + + /* Index into ISTRING. */ + int istring_index; + + /* Temporary string storage. */ + char *temp, *temp1; + + /* The text of WORD. */ + register char *string; + + /* The size of STRING. */ + size_t string_size; + + /* The index into STRING. */ + int sindex; + + /* This gets 1 if we see a $@ while quoted. */ + int quoted_dollar_at; + + /* One of UNQUOTED, PARTIALLY_QUOTED, or WHOLLY_QUOTED, depending on + whether WORD contains no quoting characters, a partially quoted + string (e.g., "xx"ab), or is fully quoted (e.g., "xxab"). */ + int quoted_state; + + /* State flags */ + int had_quoted_null; + int has_dollar_at; + int tflag; + int pflags; /* flags passed to param_expand */ + + int assignoff; /* If assignment, offset of `=' */ + + register unsigned char c; /* Current character. */ + int t_index; /* For calls to string_extract_xxx. */ + + char twochars[2]; + + DECLARE_MBSTATE; + + istring = (char *)xmalloc (istring_size = DEFAULT_INITIAL_ARRAY_SIZE); + istring[istring_index = 0] = '\0'; + quoted_dollar_at = had_quoted_null = has_dollar_at = 0; + quoted_state = UNQUOTED; + + string = word->word; + if (string == 0) + goto finished_with_string; + /* Don't need the string length for the SADD... and COPY_ macros unless + multibyte characters are possible. */ + string_size = (MB_CUR_MAX > 1) ? strlen (string) : 1; + + if (contains_dollar_at) + *contains_dollar_at = 0; + + assignoff = -1; + + /* Begin the expansion. */ + + for (sindex = 0; ;) + { + c = string[sindex]; + + /* Case on toplevel character. */ + switch (c) + { + case '\0': + goto finished_with_string; + + case CTLESC: + sindex++; +#if HANDLE_MULTIBYTE + if (MB_CUR_MAX > 1 && string[sindex]) + { + SADD_MBQCHAR_BODY(temp, string, sindex, string_size); + } + else +#endif + { + temp = (char *)xmalloc (3); + temp[0] = CTLESC; + temp[1] = c = string[sindex]; + temp[2] = '\0'; + } + +dollar_add_string: + if (string[sindex]) + sindex++; + +add_string: + if (temp) + { + istring = sub_append_string (temp, istring, &istring_index, &istring_size); + temp = (char *)0; + } + + break; + +#if defined (PROCESS_SUBSTITUTION) + /* Process substitution. */ + case '<': + case '>': + { + if (string[++sindex] != LPAREN || (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (word->flags & (W_DQUOTE|W_NOPROCSUB)) || posixly_correct) + { + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + else + t_index = sindex + 1; /* skip past both '<' and LPAREN */ + + temp1 = extract_process_subst (string, (c == '<') ? "<(" : ">(", &t_index); /*))*/ + sindex = t_index; + + /* If the process substitution specification is `<()', we want to + open the pipe for writing in the child and produce output; if + it is `>()', we want to open the pipe for reading in the child + and consume input. */ + temp = temp1 ? process_substitute (temp1, (c == '>')) : (char *)0; + + FREE (temp1); + + goto dollar_add_string; + } +#endif /* PROCESS_SUBSTITUTION */ + + case '=': + /* Posix.2 section 3.6.1 says that tildes following `=' in words + which are not assignment statements are not expanded. If the + shell isn't in posix mode, though, we perform tilde expansion + on `likely candidate' unquoted assignment statements (flags + include W_ASSIGNMENT but not W_QUOTED). A likely candidate + contains an unquoted :~ or =~. Something to think about: we + now have a flag that says to perform tilde expansion on arguments + to `assignment builtins' like declare and export that look like + assignment statements. We now do tilde expansion on such words + even in POSIX mode. */ + if (word->flags & (W_ASSIGNRHS|W_NOTILDE)) + { + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + } + /* If we're not in posix mode or forcing assignment-statement tilde + expansion, note where the `=' appears in the word and prepare to + do tilde expansion following the first `='. */ + if ((word->flags & W_ASSIGNMENT) && + (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && + assignoff == -1 && sindex > 0) + assignoff = sindex; + if (sindex == assignoff && string[sindex+1] == '~') /* XXX */ + word->flags |= W_ITILDE; +#if 0 + else if ((word->flags & W_ASSIGNMENT) && + (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && + string[sindex+1] == '~') + word->flags |= W_ITILDE; +#endif + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + + case ':': + if (word->flags & W_NOTILDE) + { + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + } + + if ((word->flags & (W_ASSIGNMENT|W_ASSIGNRHS|W_TILDEEXP)) && + string[sindex+1] == '~') + word->flags |= W_ITILDE; + + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + + case '~': + /* If the word isn't supposed to be tilde expanded, or we're not + at the start of a word or after an unquoted : or = in an + assignment statement, we don't do tilde expansion. */ + if ((word->flags & (W_NOTILDE|W_DQUOTE)) || + (sindex > 0 && ((word->flags & W_ITILDE) == 0)) || + (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + { + word->flags &= ~W_ITILDE; + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0) + goto add_ifs_character; + else + goto add_character; + } + + if (word->flags & W_ASSIGNRHS) + tflag = 2; + else if (word->flags & (W_ASSIGNMENT|W_TILDEEXP)) + tflag = 1; + else + tflag = 0; + + temp = bash_tilde_find_word (string + sindex, tflag, &t_index); + + word->flags &= ~W_ITILDE; + + if (temp && *temp && t_index > 0) + { + temp1 = bash_tilde_expand (temp, tflag); + if (temp1 && *temp1 == '~' && STREQ (temp, temp1)) + { + FREE (temp); + FREE (temp1); + goto add_character; /* tilde expansion failed */ + } + free (temp); + temp = temp1; + sindex += t_index; + goto add_quoted_string; /* XXX was add_string */ + } + else + { + FREE (temp); + goto add_character; + } + + case '$': + if (expanded_something) + *expanded_something = 1; + + has_dollar_at = 0; + pflags = (word->flags & W_NOCOMSUB) ? PF_NOCOMSUB : 0; + if (word->flags & W_NOSPLIT2) + pflags |= PF_NOSPLIT2; + tword = param_expand (string, &sindex, quoted, expanded_something, + &has_dollar_at, "ed_dollar_at, + &had_quoted_null, pflags); + + if (tword == &expand_wdesc_error || tword == &expand_wdesc_fatal) + { + free (string); + free (istring); + return ((tword == &expand_wdesc_error) ? &expand_word_error + : &expand_word_fatal); + } + if (contains_dollar_at && has_dollar_at) + *contains_dollar_at = 1; + + if (tword && (tword->flags & W_HASQUOTEDNULL)) + had_quoted_null = 1; + + temp = tword->word; + dispose_word_desc (tword); + + goto add_string; + break; + + case '`': /* Backquoted command substitution. */ + { + t_index = sindex++; + + temp = string_extract (string, &sindex, "`", SX_REQMATCH); + /* The test of sindex against t_index is to allow bare instances of + ` to pass through, for backwards compatibility. */ + if (temp == &extract_string_error || temp == &extract_string_fatal) + { + if (sindex - 1 == t_index) + { + sindex = t_index; + goto add_character; + } + report_error (_("bad substitution: no closing \"`\" in %s") , string+t_index); + free (string); + free (istring); + return ((temp == &extract_string_error) ? &expand_word_error + : &expand_word_fatal); + } + + if (expanded_something) + *expanded_something = 1; + + if (word->flags & W_NOCOMSUB) + /* sindex + 1 because string[sindex] == '`' */ + temp1 = substring (string, t_index, sindex + 1); + else + { + de_backslash (temp); + tword = command_substitute (temp, quoted); + temp1 = tword ? tword->word : (char *)NULL; + if (tword) + dispose_word_desc (tword); + } + FREE (temp); + temp = temp1; + goto dollar_add_string; + } + + case '\\': + if (string[sindex + 1] == '\n') + { + sindex += 2; + continue; + } + + c = string[++sindex]; + + if (quoted & Q_HERE_DOCUMENT) + tflag = CBSHDOC; + else if (quoted & Q_DOUBLE_QUOTES) + tflag = CBSDQUOTE; + else + tflag = 0; + + /* From Posix discussion on austin-group list: Backslash escaping + a } in ${...} is removed. Issue 0000221 */ + if ((quoted & Q_DOLBRACE) && c == RBRACE) + { + SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size); + } + else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0)) + { + SCOPY_CHAR_I (twochars, '\\', c, string, sindex, string_size); + } + else if (c == 0) + { + c = CTLNUL; + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + else + { + SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size); + } + + sindex++; +add_twochars: + /* BEFORE jumping here, we need to increment sindex if appropriate */ + RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, + DEFAULT_ARRAY_SIZE); + istring[istring_index++] = twochars[0]; + istring[istring_index++] = twochars[1]; + istring[istring_index] = '\0'; + + break; + + case '"': +#if 0 + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (word->flags & W_DQUOTE)) +#else + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) +#endif + goto add_character; + + t_index = ++sindex; + temp = string_extract_double_quoted (string, &sindex, 0); + + /* If the quotes surrounded the entire string, then the + whole word was quoted. */ + quoted_state = (t_index == 1 && string[sindex] == '\0') + ? WHOLLY_QUOTED + : PARTIALLY_QUOTED; + + if (temp && *temp) + { + tword = alloc_word_desc (); + tword->word = temp; + + temp = (char *)NULL; + + has_dollar_at = 0; + /* Need to get W_HASQUOTEDNULL flag through this function. */ + list = expand_word_internal (tword, Q_DOUBLE_QUOTES, 0, &has_dollar_at, (int *)NULL); + + if (list == &expand_word_error || list == &expand_word_fatal) + { + free (istring); + free (string); + /* expand_word_internal has already freed temp_word->word + for us because of the way it prints error messages. */ + tword->word = (char *)NULL; + dispose_word (tword); + return list; + } + + dispose_word (tword); + + /* "$@" (a double-quoted dollar-at) expands into nothing, + not even a NULL word, when there are no positional + parameters. */ + if (list == 0 && has_dollar_at) + { + quoted_dollar_at++; + break; + } + + /* If we get "$@", we know we have expanded something, so we + need to remember it for the final split on $IFS. This is + a special case; it's the only case where a quoted string + can expand into more than one word. It's going to come back + from the above call to expand_word_internal as a list with + a single word, in which all characters are quoted and + separated by blanks. What we want to do is to turn it back + into a list for the next piece of code. */ + if (list) + dequote_list (list); + + if (list && list->word && (list->word->flags & W_HASQUOTEDNULL)) + had_quoted_null = 1; + + if (has_dollar_at) + { + quoted_dollar_at++; + if (contains_dollar_at) + *contains_dollar_at = 1; + if (expanded_something) + *expanded_something = 1; + } + } + else + { + /* What we have is "". This is a minor optimization. */ + FREE (temp); + list = (WORD_LIST *)NULL; + } + + /* The code above *might* return a list (consider the case of "$@", + where it returns "$1", "$2", etc.). We can't throw away the + rest of the list, and we have to make sure each word gets added + as quoted. We test on tresult->next: if it is non-NULL, we + quote the whole list, save it to a string with string_list, and + add that string. We don't need to quote the results of this + (and it would be wrong, since that would quote the separators + as well), so we go directly to add_string. */ + if (list) + { + if (list->next) + { +#if 0 + if (quoted_dollar_at && word->flags & W_NOSPLIT2) + temp = string_list_internal (quote_list (list), " "); + else +#endif + /* Testing quoted_dollar_at makes sure that "$@" is + split correctly when $IFS does not contain a space. */ + temp = quoted_dollar_at + ? string_list_dollar_at (list, Q_DOUBLE_QUOTES) + : string_list (quote_list (list)); + dispose_words (list); + goto add_string; + } + else + { + temp = savestring (list->word->word); + tflag = list->word->flags; + dispose_words (list); + + /* If the string is not a quoted null string, we want + to remove any embedded unquoted CTLNUL characters. + We do not want to turn quoted null strings back into + the empty string, though. We do this because we + want to remove any quoted nulls from expansions that + contain other characters. For example, if we have + x"$*"y or "x$*y" and there are no positional parameters, + the $* should expand into nothing. */ + /* We use the W_HASQUOTEDNULL flag to differentiate the + cases: a quoted null character as above and when + CTLNUL is contained in the (non-null) expansion + of some variable. We use the had_quoted_null flag to + pass the value through this function to its caller. */ + if ((tflag & W_HASQUOTEDNULL) && QUOTED_NULL (temp) == 0) + remove_quoted_nulls (temp); /* XXX */ + } + } + else + temp = (char *)NULL; + + /* We do not want to add quoted nulls to strings that are only + partially quoted; we can throw them away. */ + if (temp == 0 && quoted_state == PARTIALLY_QUOTED) + continue; + + add_quoted_string: + + if (temp) + { + temp1 = temp; + temp = quote_string (temp); + free (temp1); + goto add_string; + } + else + { + /* Add NULL arg. */ + c = CTLNUL; + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + + /* break; */ + + case '\'': +#if 0 + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (word->flags & W_DQUOTE)) +#else + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) +#endif + goto add_character; + + t_index = ++sindex; + temp = string_extract_single_quoted (string, &sindex); + + /* If the entire STRING was surrounded by single quotes, + then the string is wholly quoted. */ + quoted_state = (t_index == 1 && string[sindex] == '\0') + ? WHOLLY_QUOTED + : PARTIALLY_QUOTED; + + /* If all we had was '', it is a null expansion. */ + if (*temp == '\0') + { + free (temp); + temp = (char *)NULL; + } + else + remove_quoted_escapes (temp); /* ??? */ + + /* We do not want to add quoted nulls to strings that are only + partially quoted; such nulls are discarded. */ + if (temp == 0 && (quoted_state == PARTIALLY_QUOTED)) + continue; + + /* If we have a quoted null expansion, add a quoted NULL to istring. */ + if (temp == 0) + { + c = CTLNUL; + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + else + goto add_quoted_string; + + /* break; */ + + default: + /* This is the fix for " $@ " */ + add_ifs_character: + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (isexp == 0 && isifs (c))) + { + if (string[sindex]) /* from old goto dollar_add_string */ + sindex++; + if (c == 0) + { + c = CTLNUL; + goto add_character; + } + else + { +#if HANDLE_MULTIBYTE + if (MB_CUR_MAX > 1) + sindex--; + + if (MB_CUR_MAX > 1) + { + SADD_MBQCHAR_BODY(temp, string, sindex, string_size); + } + else +#endif + { + twochars[0] = CTLESC; + twochars[1] = c; + goto add_twochars; + } + } + } + + SADD_MBCHAR (temp, string, sindex, string_size); + + add_character: + RESIZE_MALLOCED_BUFFER (istring, istring_index, 1, istring_size, + DEFAULT_ARRAY_SIZE); + istring[istring_index++] = c; + istring[istring_index] = '\0'; + + /* Next character. */ + sindex++; + } + } + +finished_with_string: + /* OK, we're ready to return. If we have a quoted string, and + quoted_dollar_at is not set, we do no splitting at all; otherwise + we split on ' '. The routines that call this will handle what to + do if nothing has been expanded. */ + + /* Partially and wholly quoted strings which expand to the empty + string are retained as an empty arguments. Unquoted strings + which expand to the empty string are discarded. The single + exception is the case of expanding "$@" when there are no + positional parameters. In that case, we discard the expansion. */ + + /* Because of how the code that handles "" and '' in partially + quoted strings works, we need to make ISTRING into a QUOTED_NULL + if we saw quoting characters, but the expansion was empty. + "" and '' are tossed away before we get to this point when + processing partially quoted strings. This makes "" and $xxx"" + equivalent when xxx is unset. We also look to see whether we + saw a quoted null from a ${} expansion and add one back if we + need to. */ + + /* If we expand to nothing and there were no single or double quotes + in the word, we throw it away. Otherwise, we return a NULL word. + The single exception is for $@ surrounded by double quotes when + there are no positional parameters. In that case, we also throw + the word away. */ + + if (*istring == '\0') + { + if (quoted_dollar_at == 0 && (had_quoted_null || quoted_state == PARTIALLY_QUOTED)) + { + istring[0] = CTLNUL; + istring[1] = '\0'; + tword = make_bare_word (istring); + tword->flags |= W_HASQUOTEDNULL; /* XXX */ + list = make_word_list (tword, (WORD_LIST *)NULL); + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + tword->flags |= W_QUOTED; + } + /* According to sh, ksh, and Posix.2, if a word expands into nothing + and a double-quoted "$@" appears anywhere in it, then the entire + word is removed. */ + else if (quoted_state == UNQUOTED || quoted_dollar_at) + list = (WORD_LIST *)NULL; +#if 0 + else + { + tword = make_bare_word (istring); + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + tword->flags |= W_QUOTED; + list = make_word_list (tword, (WORD_LIST *)NULL); + } +#else + else + list = (WORD_LIST *)NULL; +#endif + } + else if (word->flags & W_NOSPLIT) + { + tword = make_bare_word (istring); + if (word->flags & W_ASSIGNMENT) + tword->flags |= W_ASSIGNMENT; /* XXX */ + if (word->flags & W_COMPASSIGN) + tword->flags |= W_COMPASSIGN; /* XXX */ + if (word->flags & W_NOGLOB) + tword->flags |= W_NOGLOB; /* XXX */ + if (word->flags & W_NOEXPAND) + tword->flags |= W_NOEXPAND; /* XXX */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + tword->flags |= W_QUOTED; + if (had_quoted_null) + tword->flags |= W_HASQUOTEDNULL; + list = make_word_list (tword, (WORD_LIST *)NULL); + } + else + { + char *ifs_chars; + + ifs_chars = (quoted_dollar_at || has_dollar_at) ? ifs_value : (char *)NULL; + + /* If we have $@, we need to split the results no matter what. If + IFS is unset or NULL, string_list_dollar_at has separated the + positional parameters with a space, so we split on space (we have + set ifs_chars to " \t\n" above if ifs is unset). If IFS is set, + string_list_dollar_at has separated the positional parameters + with the first character of $IFS, so we split on $IFS. */ + if (has_dollar_at && ifs_chars) + list = list_string (istring, *ifs_chars ? ifs_chars : " ", 1); + else + { + tword = make_bare_word (istring); + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (quoted_state == WHOLLY_QUOTED)) + tword->flags |= W_QUOTED; + if (word->flags & W_ASSIGNMENT) + tword->flags |= W_ASSIGNMENT; + if (word->flags & W_COMPASSIGN) + tword->flags |= W_COMPASSIGN; + if (word->flags & W_NOGLOB) + tword->flags |= W_NOGLOB; + if (word->flags & W_NOEXPAND) + tword->flags |= W_NOEXPAND; + if (had_quoted_null) + tword->flags |= W_HASQUOTEDNULL; /* XXX */ + list = make_word_list (tword, (WORD_LIST *)NULL); + } + } + + free (istring); + return (list); +} + +/* **************************************************************** */ +/* */ +/* Functions for Quote Removal */ +/* */ +/* **************************************************************** */ + +/* Perform quote removal on STRING. If QUOTED > 0, assume we are obeying the + backslash quoting rules for within double quotes or a here document. */ +char * +string_quote_removal (string, quoted) + char *string; + int quoted; +{ + size_t slen; + char *r, *result_string, *temp, *send; + int sindex, tindex, dquote; + unsigned char c; + DECLARE_MBSTATE; + + /* The result can be no longer than the original string. */ + slen = strlen (string); + send = string + slen; + + r = result_string = (char *)xmalloc (slen + 1); + + for (dquote = sindex = 0; c = string[sindex];) + { + switch (c) + { + case '\\': + c = string[++sindex]; + if (c == 0) + { + *r++ = '\\'; + break; + } + if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && (sh_syntaxtab[c] & CBSDQUOTE) == 0) + *r++ = '\\'; + /* FALLTHROUGH */ + + default: + SCOPY_CHAR_M (r, string, send, sindex); + break; + + case '\'': + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) + { + *r++ = c; + sindex++; + break; + } + tindex = sindex + 1; + temp = string_extract_single_quoted (string, &tindex); + if (temp) + { + strcpy (r, temp); + r += strlen (r); + free (temp); + } + sindex = tindex; + break; + + case '"': + dquote = 1 - dquote; + sindex++; + break; + } + } + *r = '\0'; + return (result_string); +} + +#if 0 +/* UNUSED */ +/* Perform quote removal on word WORD. This allocates and returns a new + WORD_DESC *. */ +WORD_DESC * +word_quote_removal (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_DESC *w; + char *t; + + t = string_quote_removal (word->word, quoted); + w = alloc_word_desc (); + w->word = t ? t : savestring (""); + return (w); +} + +/* Perform quote removal on all words in LIST. If QUOTED is non-zero, + the members of the list are treated as if they are surrounded by + double quotes. Return a new list, or NULL if LIST is NULL. */ +WORD_LIST * +word_list_quote_removal (list, quoted) + WORD_LIST *list; + int quoted; +{ + WORD_LIST *result, *t, *tresult, *e; + + for (t = list, result = (WORD_LIST *)NULL; t; t = t->next) + { + tresult = make_word_list (word_quote_removal (t->word, quoted), (WORD_LIST *)NULL); +#if 0 + result = (WORD_LIST *) list_append (result, tresult); +#else + if (result == 0) + result = e = tresult; + else + { + e->next = tresult; + while (e->next) + e = e->next; + } +#endif + } + return (result); +} +#endif + +/******************************************* + * * + * Functions to perform word splitting * + * * + *******************************************/ + +void +setifs (v) + SHELL_VAR *v; +{ + char *t; + unsigned char uc; + + ifs_var = v; + ifs_value = (v && value_cell (v)) ? value_cell (v) : " \t\n"; + + /* Should really merge ifs_cmap with sh_syntaxtab. XXX - doesn't yet + handle multibyte chars in IFS */ + memset (ifs_cmap, '\0', sizeof (ifs_cmap)); + for (t = ifs_value ; t && *t; t++) + { + uc = *t; + ifs_cmap[uc] = 1; + } + +#if defined (HANDLE_MULTIBYTE) + if (ifs_value == 0) + { + ifs_firstc[0] = '\0'; + ifs_firstc_len = 1; + } + else + { + size_t ifs_len; + ifs_len = strnlen (ifs_value, MB_CUR_MAX); + ifs_firstc_len = MBLEN (ifs_value, ifs_len); + if (ifs_firstc_len == 1 || ifs_firstc_len == 0 || MB_INVALIDCH (ifs_firstc_len)) + { + ifs_firstc[0] = ifs_value[0]; + ifs_firstc[1] = '\0'; + ifs_firstc_len = 1; + } + else + memcpy (ifs_firstc, ifs_value, ifs_firstc_len); + } +#else + ifs_firstc = ifs_value ? *ifs_value : 0; +#endif +} + +char * +getifs () +{ + return ifs_value; +} + +/* This splits a single word into a WORD LIST on $IFS, but only if the word + is not quoted. list_string () performs quote removal for us, even if we + don't do any splitting. */ +WORD_LIST * +word_split (w, ifs_chars) + WORD_DESC *w; + char *ifs_chars; +{ + WORD_LIST *result; + + if (w) + { + char *xifs; + + xifs = ((w->flags & W_QUOTED) || ifs_chars == 0) ? "" : ifs_chars; + result = list_string (w->word, xifs, w->flags & W_QUOTED); + } + else + result = (WORD_LIST *)NULL; + + return (result); +} + +/* Perform word splitting on LIST and return the RESULT. It is possible + to return (WORD_LIST *)NULL. */ +static WORD_LIST * +word_list_split (list) + WORD_LIST *list; +{ + WORD_LIST *result, *t, *tresult, *e; + + for (t = list, result = (WORD_LIST *)NULL; t; t = t->next) + { + tresult = word_split (t->word, ifs_value); + if (result == 0) + result = e = tresult; + else + { + e->next = tresult; + while (e->next) + e = e->next; + } + } + return (result); +} + +/************************************************** + * * + * Functions to expand an entire WORD_LIST * + * * + **************************************************/ + +/* Do any word-expansion-specific cleanup and jump to top_level */ +static void +exp_jump_to_top_level (v) + int v; +{ + set_pipestatus_from_exit (last_command_exit_value); + + /* Cleanup code goes here. */ + expand_no_split_dollar_star = 0; /* XXX */ + expanding_redir = 0; + assigning_in_environment = 0; + + if (parse_and_execute_level == 0) + top_level_cleanup (); /* from sig.c */ + + jump_to_top_level (v); +} + +/* Put NLIST (which is a WORD_LIST * of only one element) at the front of + ELIST, and set ELIST to the new list. */ +#define PREPEND_LIST(nlist, elist) \ + do { nlist->next = elist; elist = nlist; } while (0) + +/* Separate out any initial variable assignments from TLIST. If set -k has + been executed, remove all assignment statements from TLIST. Initial + variable assignments and other environment assignments are placed + on SUBST_ASSIGN_VARLIST. */ +static WORD_LIST * +separate_out_assignments (tlist) + WORD_LIST *tlist; +{ + register WORD_LIST *vp, *lp; + + if (tlist == 0) + return ((WORD_LIST *)NULL); + + if (subst_assign_varlist) + dispose_words (subst_assign_varlist); /* Clean up after previous error */ + + subst_assign_varlist = (WORD_LIST *)NULL; + vp = lp = tlist; + + /* Separate out variable assignments at the start of the command. + Loop invariant: vp->next == lp + Loop postcondition: + lp = list of words left after assignment statements skipped + tlist = original list of words + */ + while (lp && (lp->word->flags & W_ASSIGNMENT)) + { + vp = lp; + lp = lp->next; + } + + /* If lp != tlist, we have some initial assignment statements. + We make SUBST_ASSIGN_VARLIST point to the list of assignment + words and TLIST point to the remaining words. */ + if (lp != tlist) + { + subst_assign_varlist = tlist; + /* ASSERT(vp->next == lp); */ + vp->next = (WORD_LIST *)NULL; /* terminate variable list */ + tlist = lp; /* remainder of word list */ + } + + /* vp == end of variable list */ + /* tlist == remainder of original word list without variable assignments */ + if (!tlist) + /* All the words in tlist were assignment statements */ + return ((WORD_LIST *)NULL); + + /* ASSERT(tlist != NULL); */ + /* ASSERT((tlist->word->flags & W_ASSIGNMENT) == 0); */ + + /* If the -k option is in effect, we need to go through the remaining + words, separate out the assignment words, and place them on + SUBST_ASSIGN_VARLIST. */ + if (place_keywords_in_env) + { + WORD_LIST *tp; /* tp == running pointer into tlist */ + + tp = tlist; + lp = tlist->next; + + /* Loop Invariant: tp->next == lp */ + /* Loop postcondition: tlist == word list without assignment statements */ + while (lp) + { + if (lp->word->flags & W_ASSIGNMENT) + { + /* Found an assignment statement, add this word to end of + subst_assign_varlist (vp). */ + if (!subst_assign_varlist) + subst_assign_varlist = vp = lp; + else + { + vp->next = lp; + vp = lp; + } + + /* Remove the word pointed to by LP from TLIST. */ + tp->next = lp->next; + /* ASSERT(vp == lp); */ + lp->next = (WORD_LIST *)NULL; + lp = tp->next; + } + else + { + tp = lp; + lp = lp->next; + } + } + } + return (tlist); +} + +#define WEXP_VARASSIGN 0x001 +#define WEXP_BRACEEXP 0x002 +#define WEXP_TILDEEXP 0x004 +#define WEXP_PARAMEXP 0x008 +#define WEXP_PATHEXP 0x010 + +/* All of the expansions, including variable assignments at the start of + the list. */ +#define WEXP_ALL (WEXP_VARASSIGN|WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP) + +/* All of the expansions except variable assignments at the start of + the list. */ +#define WEXP_NOVARS (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP) + +/* All of the `shell expansions': brace expansion, tilde expansion, parameter + expansion, command substitution, arithmetic expansion, word splitting, and + quote removal. */ +#define WEXP_SHELLEXP (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP) + +/* Take the list of words in LIST and do the various substitutions. Return + a new list of words which is the expanded list, and without things like + variable assignments. */ + +WORD_LIST * +expand_words (list) + WORD_LIST *list; +{ + return (expand_word_list_internal (list, WEXP_ALL)); +} + +/* Same as expand_words (), but doesn't hack variable or environment + variables. */ +WORD_LIST * +expand_words_no_vars (list) + WORD_LIST *list; +{ + return (expand_word_list_internal (list, WEXP_NOVARS)); +} + +WORD_LIST * +expand_words_shellexp (list) + WORD_LIST *list; +{ + return (expand_word_list_internal (list, WEXP_SHELLEXP)); +} + +static WORD_LIST * +glob_expand_word_list (tlist, eflags) + WORD_LIST *tlist; + int eflags; +{ + char **glob_array, *temp_string; + register int glob_index; + WORD_LIST *glob_list, *output_list, *disposables, *next; + WORD_DESC *tword; + + output_list = disposables = (WORD_LIST *)NULL; + glob_array = (char **)NULL; + while (tlist) + { + /* For each word, either globbing is attempted or the word is + added to orig_list. If globbing succeeds, the results are + added to orig_list and the word (tlist) is added to the list + of disposable words. If globbing fails and failed glob + expansions are left unchanged (the shell default), the + original word is added to orig_list. If globbing fails and + failed glob expansions are removed, the original word is + added to the list of disposable words. orig_list ends up + in reverse order and requires a call to REVERSE_LIST to + be set right. After all words are examined, the disposable + words are freed. */ + next = tlist->next; + + /* If the word isn't an assignment and contains an unquoted + pattern matching character, then glob it. */ + if ((tlist->word->flags & W_NOGLOB) == 0 && + unquoted_glob_pattern_p (tlist->word->word)) + { + glob_array = shell_glob_filename (tlist->word->word); + + /* Handle error cases. + I don't think we should report errors like "No such file + or directory". However, I would like to report errors + like "Read failed". */ + + if (glob_array == 0 || GLOB_FAILED (glob_array)) + { + glob_array = (char **)xmalloc (sizeof (char *)); + glob_array[0] = (char *)NULL; + } + + /* Dequote the current word in case we have to use it. */ + if (glob_array[0] == NULL) + { + temp_string = dequote_string (tlist->word->word); + free (tlist->word->word); + tlist->word->word = temp_string; + } + + /* Make the array into a word list. */ + glob_list = (WORD_LIST *)NULL; + for (glob_index = 0; glob_array[glob_index]; glob_index++) + { + tword = make_bare_word (glob_array[glob_index]); + tword->flags |= W_GLOBEXP; /* XXX */ + glob_list = make_word_list (tword, glob_list); + } + + if (glob_list) + { + output_list = (WORD_LIST *)list_append (glob_list, output_list); + PREPEND_LIST (tlist, disposables); + } + else if (fail_glob_expansion != 0) + { + report_error (_("no match: %s"), tlist->word->word); + exp_jump_to_top_level (DISCARD); + } + else if (allow_null_glob_expansion == 0) + { + /* Failed glob expressions are left unchanged. */ + PREPEND_LIST (tlist, output_list); + } + else + { + /* Failed glob expressions are removed. */ + PREPEND_LIST (tlist, disposables); + } + } + else + { + /* Dequote the string. */ + temp_string = dequote_string (tlist->word->word); + free (tlist->word->word); + tlist->word->word = temp_string; + PREPEND_LIST (tlist, output_list); + } + + strvec_dispose (glob_array); + glob_array = (char **)NULL; + + tlist = next; + } + + if (disposables) + dispose_words (disposables); + + if (output_list) + output_list = REVERSE_LIST (output_list, WORD_LIST *); + + return (output_list); +} + +#if defined (BRACE_EXPANSION) +static WORD_LIST * +brace_expand_word_list (tlist, eflags) + WORD_LIST *tlist; + int eflags; +{ + register char **expansions; + char *temp_string; + WORD_LIST *disposables, *output_list, *next; + WORD_DESC *w; + int eindex; + + for (disposables = output_list = (WORD_LIST *)NULL; tlist; tlist = next) + { + next = tlist->next; + + if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG)) + { +/*itrace("brace_expand_word_list: %s: W_COMPASSIGN|W_ASSIGNARG", tlist->word->word);*/ + PREPEND_LIST (tlist, output_list); + continue; + } + + /* Only do brace expansion if the word has a brace character. If + not, just add the word list element to BRACES and continue. In + the common case, at least when running shell scripts, this will + degenerate to a bunch of calls to `mbschr', and then what is + basically a reversal of TLIST into BRACES, which is corrected + by a call to REVERSE_LIST () on BRACES when the end of TLIST + is reached. */ + if (mbschr (tlist->word->word, LBRACE)) + { + expansions = brace_expand (tlist->word->word); + + for (eindex = 0; temp_string = expansions[eindex]; eindex++) + { + w = make_word (temp_string); + /* If brace expansion didn't change the word, preserve + the flags. We may want to preserve the flags + unconditionally someday -- XXX */ + if (STREQ (temp_string, tlist->word->word)) + w->flags = tlist->word->flags; + output_list = make_word_list (w, output_list); + free (expansions[eindex]); + } + free (expansions); + + /* Add TLIST to the list of words to be freed after brace + expansion has been performed. */ + PREPEND_LIST (tlist, disposables); + } + else + PREPEND_LIST (tlist, output_list); + } + + if (disposables) + dispose_words (disposables); + + if (output_list) + output_list = REVERSE_LIST (output_list, WORD_LIST *); + + return (output_list); +} +#endif + +#if defined (ARRAY_VARS) +/* Take WORD, a compound associative array assignment, and internally run + 'declare -A w', where W is the variable name portion of WORD. */ +static int +make_internal_declare (word, option) + char *word; + char *option; +{ + int t; + WORD_LIST *wl; + WORD_DESC *w; + + w = make_word (word); + + t = assignment (w->word, 0); + w->word[t] = '\0'; + + wl = make_word_list (w, (WORD_LIST *)NULL); + wl = make_word_list (make_word (option), wl); + + return (declare_builtin (wl)); +} +#endif + +static WORD_LIST * +shell_expand_word_list (tlist, eflags) + WORD_LIST *tlist; + int eflags; +{ + WORD_LIST *expanded, *orig_list, *new_list, *next, *temp_list; + int expanded_something, has_dollar_at; + char *temp_string; + + /* We do tilde expansion all the time. This is what 1003.2 says. */ + new_list = (WORD_LIST *)NULL; + for (orig_list = tlist; tlist; tlist = next) + { + temp_string = tlist->word->word; + + next = tlist->next; + +#if defined (ARRAY_VARS) + /* If this is a compound array assignment to a builtin that accepts + such assignments (e.g., `declare'), take the assignment and perform + it separately, handling the semantics of declarations inside shell + functions. This avoids the double-evaluation of such arguments, + because `declare' does some evaluation of compound assignments on + its own. */ + if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG)) + { + int t; + + if (tlist->word->flags & W_ASSIGNASSOC) + make_internal_declare (tlist->word->word, "-A"); + + t = do_word_assignment (tlist->word); + if (t == 0) + { + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level (DISCARD); + } + + /* Now transform the word as ksh93 appears to do and go on */ + t = assignment (tlist->word->word, 0); + tlist->word->word[t] = '\0'; + tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC); + } +#endif + + expanded_something = 0; + expanded = expand_word_internal + (tlist->word, 0, 0, &has_dollar_at, &expanded_something); + + if (expanded == &expand_word_error || expanded == &expand_word_fatal) + { + /* By convention, each time this error is returned, + tlist->word->word has already been freed. */ + tlist->word->word = (char *)NULL; + + /* Dispose our copy of the original list. */ + dispose_words (orig_list); + /* Dispose the new list we're building. */ + dispose_words (new_list); + + last_command_exit_value = EXECUTION_FAILURE; + if (expanded == &expand_word_error) + exp_jump_to_top_level (DISCARD); + else + exp_jump_to_top_level (FORCE_EOF); + } + + /* Don't split words marked W_NOSPLIT. */ + if (expanded_something && (tlist->word->flags & W_NOSPLIT) == 0) + { + temp_list = word_list_split (expanded); + dispose_words (expanded); + } + else + { + /* If no parameter expansion, command substitution, process + substitution, or arithmetic substitution took place, then + do not do word splitting. We still have to remove quoted + null characters from the result. */ + word_list_remove_quoted_nulls (expanded); + temp_list = expanded; + } + + expanded = REVERSE_LIST (temp_list, WORD_LIST *); + new_list = (WORD_LIST *)list_append (expanded, new_list); + } + + if (orig_list) + dispose_words (orig_list); + + if (new_list) + new_list = REVERSE_LIST (new_list, WORD_LIST *); + + return (new_list); +} + +/* The workhorse for expand_words () and expand_words_no_vars (). + First arg is LIST, a WORD_LIST of words. + Second arg EFLAGS is a flags word controlling which expansions are + performed. + + This does all of the substitutions: brace expansion, tilde expansion, + parameter expansion, command substitution, arithmetic expansion, + process substitution, word splitting, and pathname expansion, according + to the bits set in EFLAGS. Words with the W_QUOTED or W_NOSPLIT bits + set, or for which no expansion is done, do not undergo word splitting. + Words with the W_NOGLOB bit set do not undergo pathname expansion. */ +static WORD_LIST * +expand_word_list_internal (list, eflags) + WORD_LIST *list; + int eflags; +{ + WORD_LIST *new_list, *temp_list; + int tint; + + if (list == 0) + return ((WORD_LIST *)NULL); + + garglist = new_list = copy_word_list (list); + if (eflags & WEXP_VARASSIGN) + { + garglist = new_list = separate_out_assignments (new_list); + if (new_list == 0) + { + if (subst_assign_varlist) + { + /* All the words were variable assignments, so they are placed + into the shell's environment. */ + for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next) + { + this_command_name = (char *)NULL; /* no arithmetic errors */ + tint = do_word_assignment (temp_list->word); + /* Variable assignment errors in non-interactive shells + running in Posix.2 mode cause the shell to exit. */ + if (tint == 0) + { + last_command_exit_value = EXECUTION_FAILURE; + if (interactive_shell == 0 && posixly_correct) + exp_jump_to_top_level (FORCE_EOF); + else + exp_jump_to_top_level (DISCARD); + } + } + dispose_words (subst_assign_varlist); + subst_assign_varlist = (WORD_LIST *)NULL; + } + return ((WORD_LIST *)NULL); + } + } + + /* Begin expanding the words that remain. The expansions take place on + things that aren't really variable assignments. */ + +#if defined (BRACE_EXPANSION) + /* Do brace expansion on this word if there are any brace characters + in the string. */ + if ((eflags & WEXP_BRACEEXP) && brace_expansion && new_list) + new_list = brace_expand_word_list (new_list, eflags); +#endif /* BRACE_EXPANSION */ + + /* Perform the `normal' shell expansions: tilde expansion, parameter and + variable substitution, command substitution, arithmetic expansion, + and word splitting. */ + new_list = shell_expand_word_list (new_list, eflags); + + /* Okay, we're almost done. Now let's just do some filename + globbing. */ + if (new_list) + { + if ((eflags & WEXP_PATHEXP) && disallow_filename_globbing == 0) + /* Glob expand the word list unless globbing has been disabled. */ + new_list = glob_expand_word_list (new_list, eflags); + else + /* Dequote the words, because we're not performing globbing. */ + new_list = dequote_list (new_list); + } + + if ((eflags & WEXP_VARASSIGN) && subst_assign_varlist) + { + sh_wassign_func_t *assign_func; + + /* If the remainder of the words expand to nothing, Posix.2 requires + that the variable and environment assignments affect the shell's + environment. */ + assign_func = new_list ? assign_in_env : do_word_assignment; + tempenv_assign_error = 0; + + for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next) + { + this_command_name = (char *)NULL; + assigning_in_environment = (assign_func == assign_in_env); + tint = (*assign_func) (temp_list->word); + assigning_in_environment = 0; + /* Variable assignment errors in non-interactive shells running + in Posix.2 mode cause the shell to exit. */ + if (tint == 0) + { + if (assign_func == do_word_assignment) + { + last_command_exit_value = EXECUTION_FAILURE; + if (interactive_shell == 0 && posixly_correct) + exp_jump_to_top_level (FORCE_EOF); + else + exp_jump_to_top_level (DISCARD); + } + else + tempenv_assign_error++; + } + } + + dispose_words (subst_assign_varlist); + subst_assign_varlist = (WORD_LIST *)NULL; + } + +#if 0 + tint = list_length (new_list) + 1; + RESIZE_MALLOCED_BUFFER (glob_argv_flags, 0, tint, glob_argv_flags_size, 16); + for (tint = 0, temp_list = new_list; temp_list; temp_list = temp_list->next) + glob_argv_flags[tint++] = (temp_list->word->flags & W_GLOBEXP) ? '1' : '0'; + glob_argv_flags[tint] = '\0'; +#endif + + return (new_list); +} diff --git a/subst.c.save2 b/subst.c.save2 new file mode 100644 index 00000000..a6be67af --- /dev/null +++ b/subst.c.save2 @@ -0,0 +1,9493 @@ +/* subst.c -- The part of the shell that does parameter, command, arithmetic, + and globbing substitutions. */ + +/* ``Have a little faith, there's magic in the night. You ain't a + beauty, but, hey, you're alright.'' */ + +/* Copyright (C) 1987-2010 Free Software Foundation, Inc. + + This file is part of GNU Bash, the Bourne Again SHell. + + Bash is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bash is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bash. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "config.h" + +#include "bashtypes.h" +#include <stdio.h> +#include "chartypes.h" +#if defined (HAVE_PWD_H) +# include <pwd.h> +#endif +#include <signal.h> +#include <errno.h> + +#if defined (HAVE_UNISTD_H) +# include <unistd.h> +#endif + +#include "bashansi.h" +#include "posixstat.h" +#include "bashintl.h" + +#include "shell.h" +#include "parser.h" +#include "flags.h" +#include "jobs.h" +#include "execute_cmd.h" +#include "filecntl.h" +#include "trap.h" +#include "pathexp.h" +#include "mailcheck.h" + +#include "shmbutil.h" + +#include "builtins/getopt.h" +#include "builtins/common.h" + +#include "builtins/builtext.h" + +#include <tilde/tilde.h> +#include <glob/strmatch.h> + +#if !defined (errno) +extern int errno; +#endif /* !errno */ + +/* The size that strings change by. */ +#define DEFAULT_INITIAL_ARRAY_SIZE 112 +#define DEFAULT_ARRAY_SIZE 128 + +/* Variable types. */ +#define VT_VARIABLE 0 +#define VT_POSPARMS 1 +#define VT_ARRAYVAR 2 +#define VT_ARRAYMEMBER 3 +#define VT_ASSOCVAR 4 + +#define VT_STARSUB 128 /* $* or ${array[*]} -- used to split */ + +/* Flags for quoted_strchr */ +#define ST_BACKSL 0x01 +#define ST_CTLESC 0x02 +#define ST_SQUOTE 0x04 /* unused yet */ +#define ST_DQUOTE 0x08 /* unused yet */ + +/* Flags for the `pflags' argument to param_expand() */ +#define PF_NOCOMSUB 0x01 /* Do not perform command substitution */ +#define PF_IGNUNBOUND 0x02 /* ignore unbound vars even if -u set */ +#define PF_NOSPLIT2 0x04 /* same as W_NOSPLIT2 */ + +/* These defs make it easier to use the editor. */ +#define LBRACE '{' +#define RBRACE '}' +#define LPAREN '(' +#define RPAREN ')' + +#if defined (HANDLE_MULTIBYTE) +#define WLPAREN L'(' +#define WRPAREN L')' +#endif + +/* Evaluates to 1 if C is one of the shell's special parameters whose length + can be taken, but is also one of the special expansion characters. */ +#define VALID_SPECIAL_LENGTH_PARAM(c) \ + ((c) == '-' || (c) == '?' || (c) == '#') + +/* Evaluates to 1 if C is one of the shell's special parameters for which an + indirect variable reference may be made. */ +#define VALID_INDIR_PARAM(c) \ + ((c) == '#' || (c) == '?' || (c) == '@' || (c) == '*') + +/* Evaluates to 1 if C is one of the OP characters that follows the parameter + in ${parameter[:]OPword}. */ +#define VALID_PARAM_EXPAND_CHAR(c) (sh_syntaxtab[(unsigned char)c] & CSUBSTOP) + +/* Evaluates to 1 if this is one of the shell's special variables. */ +#define SPECIAL_VAR(name, wi) \ + ((DIGIT (*name) && all_digits (name)) || \ + (name[1] == '\0' && (sh_syntaxtab[(unsigned char)*name] & CSPECVAR)) || \ + (wi && name[2] == '\0' && VALID_INDIR_PARAM (name[1]))) + +/* An expansion function that takes a string and a quoted flag and returns + a WORD_LIST *. Used as the type of the third argument to + expand_string_if_necessary(). */ +typedef WORD_LIST *EXPFUNC __P((char *, int)); + +/* Process ID of the last command executed within command substitution. */ +pid_t last_command_subst_pid = NO_PID; +pid_t current_command_subst_pid = NO_PID; + +/* Variables used to keep track of the characters in IFS. */ +SHELL_VAR *ifs_var; +char *ifs_value; +unsigned char ifs_cmap[UCHAR_MAX + 1]; + +#if defined (HANDLE_MULTIBYTE) +unsigned char ifs_firstc[MB_LEN_MAX]; +size_t ifs_firstc_len; +#else +unsigned char ifs_firstc; +#endif + +/* Sentinel to tell when we are performing variable assignments preceding a + command name and putting them into the environment. Used to make sure + we use the temporary environment when looking up variable values. */ +int assigning_in_environment; + +/* Used to hold a list of variable assignments preceding a command. Global + so the SIGCHLD handler in jobs.c can unwind-protect it when it runs a + SIGCHLD trap and so it can be saved and restored by the trap handlers. */ +WORD_LIST *subst_assign_varlist = (WORD_LIST *)NULL; + +/* Extern functions and variables from different files. */ +extern int last_command_exit_value, last_command_exit_signal; +extern int subshell_environment, line_number; +extern int subshell_level, parse_and_execute_level, sourcelevel; +extern int eof_encountered; +extern int return_catch_flag, return_catch_value; +extern pid_t dollar_dollar_pid; +extern int posixly_correct; +extern char *this_command_name; +extern struct fd_bitmap *current_fds_to_close; +extern int wordexp_only; +extern int expanding_redir; +extern int tempenv_assign_error; + +#if !defined (HAVE_WCSDUP) && defined (HANDLE_MULTIBYTE) +extern wchar_t *wcsdup __P((const wchar_t *)); +#endif + +/* Non-zero means to allow unmatched globbed filenames to expand to + a null file. */ +int allow_null_glob_expansion; + +/* Non-zero means to throw an error when globbing fails to match anything. */ +int fail_glob_expansion; + +#if 0 +/* Variables to keep track of which words in an expanded word list (the + output of expand_word_list_internal) are the result of globbing + expansions. GLOB_ARGV_FLAGS is used by execute_cmd.c. + (CURRENTLY UNUSED). */ +char *glob_argv_flags; +static int glob_argv_flags_size; +#endif + +static WORD_LIST expand_word_error, expand_word_fatal; +static WORD_DESC expand_wdesc_error, expand_wdesc_fatal; +static char expand_param_error, expand_param_fatal; +static char extract_string_error, extract_string_fatal; + +/* Tell the expansion functions to not longjmp back to top_level on fatal + errors. Enabled when doing completion and prompt string expansion. */ +static int no_longjmp_on_fatal_error = 0; + +/* Set by expand_word_unsplit; used to inhibit splitting and re-joining + $* on $IFS, primarily when doing assignment statements. */ +static int expand_no_split_dollar_star = 0; + +/* A WORD_LIST of words to be expanded by expand_word_list_internal, + without any leading variable assignments. */ +static WORD_LIST *garglist = (WORD_LIST *)NULL; + +static char *quoted_substring __P((char *, int, int)); +static int quoted_strlen __P((char *)); +static char *quoted_strchr __P((char *, int, int)); + +static char *expand_string_if_necessary __P((char *, int, EXPFUNC *)); +static inline char *expand_string_to_string_internal __P((char *, int, EXPFUNC *)); +static WORD_LIST *call_expand_word_internal __P((WORD_DESC *, int, int, int *, int *)); +static WORD_LIST *expand_string_internal __P((char *, int)); +static WORD_LIST *expand_string_leave_quoted __P((char *, int)); +static WORD_LIST *expand_string_for_rhs __P((char *, int, int *, int *)); + +static WORD_LIST *list_quote_escapes __P((WORD_LIST *)); +static char *make_quoted_char __P((int)); +static WORD_LIST *quote_list __P((WORD_LIST *)); + +static int unquoted_substring __P((char *, char *)); +static int unquoted_member __P((int, char *)); + +#if defined (ARRAY_VARS) +static SHELL_VAR *do_compound_assignment __P((char *, char *, int)); +#endif +static int do_assignment_internal __P((const WORD_DESC *, int)); + +static char *string_extract_verbatim __P((char *, size_t, int *, char *, int)); +static char *string_extract __P((char *, int *, char *, int)); +static char *string_extract_double_quoted __P((char *, int *, int)); +static inline char *string_extract_single_quoted __P((char *, int *)); +static inline int skip_single_quoted __P((const char *, size_t, int)); +static int skip_double_quoted __P((char *, size_t, int)); +static char *extract_delimited_string __P((char *, int *, char *, char *, char *, int)); +static char *extract_dollar_brace_string __P((char *, int *, int, int)); +static int skip_matched_pair __P((const char *, int, int, int, int)); + +static char *pos_params __P((char *, int, int, int)); + +static unsigned char *mb_getcharlens __P((char *, int)); + +static char *remove_upattern __P((char *, char *, int)); +#if defined (HANDLE_MULTIBYTE) +static wchar_t *remove_wpattern __P((wchar_t *, size_t, wchar_t *, int)); +#endif +static char *remove_pattern __P((char *, char *, int)); + +static int match_pattern_char __P((char *, char *)); +static int match_upattern __P((char *, char *, int, char **, char **)); +#if defined (HANDLE_MULTIBYTE) +static int match_pattern_wchar __P((wchar_t *, wchar_t *)); +static int match_wpattern __P((wchar_t *, char **, size_t, wchar_t *, int, char **, char **)); +#endif +static int match_pattern __P((char *, char *, int, char **, char **)); +static int getpatspec __P((int, char *)); +static char *getpattern __P((char *, int, int)); +static char *variable_remove_pattern __P((char *, char *, int, int)); +static char *list_remove_pattern __P((WORD_LIST *, char *, int, int, int)); +static char *parameter_list_remove_pattern __P((int, char *, int, int)); +#ifdef ARRAY_VARS +static char *array_remove_pattern __P((SHELL_VAR *, char *, int, char *, int)); +#endif +static char *parameter_brace_remove_pattern __P((char *, char *, int, char *, int, int, int)); + +static char *process_substitute __P((char *, int)); + +static char *read_comsub __P((int, int, int *)); + +#ifdef ARRAY_VARS +static arrayind_t array_length_reference __P((char *)); +#endif + +static int valid_brace_expansion_word __P((char *, int)); +static int chk_atstar __P((char *, int, int *, int *)); +static int chk_arithsub __P((const char *, int)); + +static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int, arrayind_t *)); +static WORD_DESC *parameter_brace_expand_indir __P((char *, int, int, int *, int *)); +static WORD_DESC *parameter_brace_expand_rhs __P((char *, char *, int, int, int *, int *)); +static void parameter_brace_expand_error __P((char *, char *)); + +static int valid_length_expression __P((char *)); +static intmax_t parameter_brace_expand_length __P((char *)); + +static char *skiparith __P((char *, int)); +static int verify_substring_values __P((SHELL_VAR *, char *, char *, int, intmax_t *, intmax_t *)); +static int get_var_and_type __P((char *, char *, arrayind_t, int, int, SHELL_VAR **, char **)); +static char *mb_substring __P((char *, int, int)); +static char *parameter_brace_substring __P((char *, char *, int, char *, int, int)); + +static int shouldexp_replacement __P((char *)); + +static char *pos_params_pat_subst __P((char *, char *, char *, int)); + +static char *parameter_brace_patsub __P((char *, char *, int, char *, int, int)); + +static char *pos_params_casemod __P((char *, char *, int, int)); +static char *parameter_brace_casemod __P((char *, char *, int, int, char *, int, int)); + +static WORD_DESC *parameter_brace_expand __P((char *, int *, int, int, int *, int *)); +static WORD_DESC *param_expand __P((char *, int *, int, int *, int *, int *, int *, int)); + +static WORD_LIST *expand_word_internal __P((WORD_DESC *, int, int, int *, int *)); + +static WORD_LIST *word_list_split __P((WORD_LIST *)); + +static void exp_jump_to_top_level __P((int)); + +static WORD_LIST *separate_out_assignments __P((WORD_LIST *)); +static WORD_LIST *glob_expand_word_list __P((WORD_LIST *, int)); +#ifdef BRACE_EXPANSION +static WORD_LIST *brace_expand_word_list __P((WORD_LIST *, int)); +#endif +#if defined (ARRAY_VARS) +static int make_internal_declare __P((char *, char *)); +#endif +static WORD_LIST *shell_expand_word_list __P((WORD_LIST *, int)); +static WORD_LIST *expand_word_list_internal __P((WORD_LIST *, int)); + +/* **************************************************************** */ +/* */ +/* Utility Functions */ +/* */ +/* **************************************************************** */ + +#if defined (DEBUG) +void +dump_word_flags (flags) + int flags; +{ + int f; + + f = flags; + fprintf (stderr, "%d -> ", f); + if (f & W_ASSIGNASSOC) + { + f &= ~W_ASSIGNASSOC; + fprintf (stderr, "W_ASSIGNASSOC%s", f ? "|" : ""); + } + if (f & W_HASCTLESC) + { + f &= ~W_HASCTLESC; + fprintf (stderr, "W_HASCTLESC%s", f ? "|" : ""); + } + if (f & W_NOPROCSUB) + { + f &= ~W_NOPROCSUB; + fprintf (stderr, "W_NOPROCSUB%s", f ? "|" : ""); + } + if (f & W_DQUOTE) + { + f &= ~W_DQUOTE; + fprintf (stderr, "W_DQUOTE%s", f ? "|" : ""); + } + if (f & W_HASQUOTEDNULL) + { + f &= ~W_HASQUOTEDNULL; + fprintf (stderr, "W_HASQUOTEDNULL%s", f ? "|" : ""); + } + if (f & W_ASSIGNARG) + { + f &= ~W_ASSIGNARG; + fprintf (stderr, "W_ASSIGNARG%s", f ? "|" : ""); + } + if (f & W_ASSNBLTIN) + { + f &= ~W_ASSNBLTIN; + fprintf (stderr, "W_ASSNBLTIN%s", f ? "|" : ""); + } + if (f & W_COMPASSIGN) + { + f &= ~W_COMPASSIGN; + fprintf (stderr, "W_COMPASSIGN%s", f ? "|" : ""); + } + if (f & W_NOEXPAND) + { + f &= ~W_NOEXPAND; + fprintf (stderr, "W_NOEXPAND%s", f ? "|" : ""); + } + if (f & W_ITILDE) + { + f &= ~W_ITILDE; + fprintf (stderr, "W_ITILDE%s", f ? "|" : ""); + } + if (f & W_NOTILDE) + { + f &= ~W_NOTILDE; + fprintf (stderr, "W_NOTILDE%s", f ? "|" : ""); + } + if (f & W_ASSIGNRHS) + { + f &= ~W_ASSIGNRHS; + fprintf (stderr, "W_ASSIGNRHS%s", f ? "|" : ""); + } + if (f & W_NOCOMSUB) + { + f &= ~W_NOCOMSUB; + fprintf (stderr, "W_NOCOMSUB%s", f ? "|" : ""); + } + if (f & W_DOLLARSTAR) + { + f &= ~W_DOLLARSTAR; + fprintf (stderr, "W_DOLLARSTAR%s", f ? "|" : ""); + } + if (f & W_DOLLARAT) + { + f &= ~W_DOLLARAT; + fprintf (stderr, "W_DOLLARAT%s", f ? "|" : ""); + } + if (f & W_TILDEEXP) + { + f &= ~W_TILDEEXP; + fprintf (stderr, "W_TILDEEXP%s", f ? "|" : ""); + } + if (f & W_NOSPLIT2) + { + f &= ~W_NOSPLIT2; + fprintf (stderr, "W_NOSPLIT2%s", f ? "|" : ""); + } + if (f & W_NOGLOB) + { + f &= ~W_NOGLOB; + fprintf (stderr, "W_NOGLOB%s", f ? "|" : ""); + } + if (f & W_NOSPLIT) + { + f &= ~W_NOSPLIT; + fprintf (stderr, "W_NOSPLIT%s", f ? "|" : ""); + } + if (f & W_GLOBEXP) + { + f &= ~W_GLOBEXP; + fprintf (stderr, "W_GLOBEXP%s", f ? "|" : ""); + } + if (f & W_ASSIGNMENT) + { + f &= ~W_ASSIGNMENT; + fprintf (stderr, "W_ASSIGNMENT%s", f ? "|" : ""); + } + if (f & W_QUOTED) + { + f &= ~W_QUOTED; + fprintf (stderr, "W_QUOTED%s", f ? "|" : ""); + } + if (f & W_HASDOLLAR) + { + f &= ~W_HASDOLLAR; + fprintf (stderr, "W_HASDOLLAR%s", f ? "|" : ""); + } + fprintf (stderr, "\n"); + fflush (stderr); +} +#endif + +#ifdef INCLUDE_UNUSED +static char * +quoted_substring (string, start, end) + char *string; + int start, end; +{ + register int len, l; + register char *result, *s, *r; + + len = end - start; + + /* Move to string[start], skipping quoted characters. */ + for (s = string, l = 0; *s && l < start; ) + { + if (*s == CTLESC) + { + s++; + continue; + } + l++; + if (*s == 0) + break; + } + + r = result = (char *)xmalloc (2*len + 1); /* save room for quotes */ + + /* Copy LEN characters, including quote characters. */ + s = string + l; + for (l = 0; l < len; s++) + { + if (*s == CTLESC) + *r++ = *s++; + *r++ = *s; + l++; + if (*s == 0) + break; + } + *r = '\0'; + return result; +} +#endif + +#ifdef INCLUDE_UNUSED +/* Return the length of S, skipping over quoted characters */ +static int +quoted_strlen (s) + char *s; +{ + register char *p; + int i; + + i = 0; + for (p = s; *p; p++) + { + if (*p == CTLESC) + { + p++; + if (*p == 0) + return (i + 1); + } + i++; + } + + return i; +} +#endif + +/* Find the first occurrence of character C in string S, obeying shell + quoting rules. If (FLAGS & ST_BACKSL) is non-zero, backslash-escaped + characters are skipped. If (FLAGS & ST_CTLESC) is non-zero, characters + escaped with CTLESC are skipped. */ +static char * +quoted_strchr (s, c, flags) + char *s; + int c, flags; +{ + register char *p; + + for (p = s; *p; p++) + { + if (((flags & ST_BACKSL) && *p == '\\') + || ((flags & ST_CTLESC) && *p == CTLESC)) + { + p++; + if (*p == '\0') + return ((char *)NULL); + continue; + } + else if (*p == c) + return p; + } + return ((char *)NULL); +} + +/* Return 1 if CHARACTER appears in an unquoted portion of + STRING. Return 0 otherwise. CHARACTER must be a single-byte character. */ +static int +unquoted_member (character, string) + int character; + char *string; +{ + size_t slen; + int sindex, c; + DECLARE_MBSTATE; + + slen = strlen (string); + sindex = 0; + while (c = string[sindex]) + { + if (c == character) + return (1); + + switch (c) + { + default: + ADVANCE_CHAR (string, slen, sindex); + break; + + case '\\': + sindex++; + if (string[sindex]) + ADVANCE_CHAR (string, slen, sindex); + break; + + case '\'': + sindex = skip_single_quoted (string, slen, ++sindex); + break; + + case '"': + sindex = skip_double_quoted (string, slen, ++sindex); + break; + } + } + return (0); +} + +/* Return 1 if SUBSTR appears in an unquoted portion of STRING. */ +static int +unquoted_substring (substr, string) + char *substr, *string; +{ + size_t slen; + int sindex, c, sublen; + DECLARE_MBSTATE; + + if (substr == 0 || *substr == '\0') + return (0); + + slen = strlen (string); + sublen = strlen (substr); + for (sindex = 0; c = string[sindex]; ) + { + if (STREQN (string + sindex, substr, sublen)) + return (1); + + switch (c) + { + case '\\': + sindex++; + + if (string[sindex]) + ADVANCE_CHAR (string, slen, sindex); + break; + + case '\'': + sindex = skip_single_quoted (string, slen, ++sindex); + break; + + case '"': + sindex = skip_double_quoted (string, slen, ++sindex); + break; + + default: + ADVANCE_CHAR (string, slen, sindex); + break; + } + } + return (0); +} + +/* Most of the substitutions must be done in parallel. In order + to avoid using tons of unclear goto's, I have some functions + for manipulating malloc'ed strings. They all take INDX, a + pointer to an integer which is the offset into the string + where manipulation is taking place. They also take SIZE, a + pointer to an integer which is the current length of the + character array for this string. */ + +/* Append SOURCE to TARGET at INDEX. SIZE is the current amount + of space allocated to TARGET. SOURCE can be NULL, in which + case nothing happens. Gets rid of SOURCE by freeing it. + Returns TARGET in case the location has changed. */ +INLINE char * +sub_append_string (source, target, indx, size) + char *source, *target; + int *indx, *size; +{ + if (source) + { + int srclen, n; + + srclen = STRLEN (source); + if (srclen >= (int)(*size - *indx)) + { + n = srclen + *indx; + n = (n + DEFAULT_ARRAY_SIZE) - (n % DEFAULT_ARRAY_SIZE); + target = (char *)xrealloc (target, (*size = n)); + } + + FASTCOPY (source, target + *indx, srclen); + *indx += srclen; + target[*indx] = '\0'; + + free (source); + } + return (target); +} + +#if 0 +/* UNUSED */ +/* Append the textual representation of NUMBER to TARGET. + INDX and SIZE are as in SUB_APPEND_STRING. */ +char * +sub_append_number (number, target, indx, size) + intmax_t number; + int *indx, *size; + char *target; +{ + char *temp; + + temp = itos (number); + return (sub_append_string (temp, target, indx, size)); +} +#endif + +/* Extract a substring from STRING, starting at SINDEX and ending with + one of the characters in CHARLIST. Don't make the ending character + part of the string. Leave SINDEX pointing at the ending character. + Understand about backslashes in the string. If (flags & SX_VARNAME) + is non-zero, and array variables have been compiled into the shell, + everything between a `[' and a corresponding `]' is skipped over. + If (flags & SX_NOALLOC) is non-zero, don't return the substring, just + update SINDEX. If (flags & SX_REQMATCH) is non-zero, the string must + contain a closing character from CHARLIST. */ +static char * +string_extract (string, sindex, charlist, flags) + char *string; + int *sindex; + char *charlist; + int flags; +{ + register int c, i; + int found; + size_t slen; + char *temp; + DECLARE_MBSTATE; + + slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 0; + i = *sindex; + found = 0; + while (c = string[i]) + { + if (c == '\\') + { + if (string[i + 1]) + i++; + else + break; + } +#if defined (ARRAY_VARS) + else if ((flags & SX_VARNAME) && c == '[') + { + int ni; + /* If this is an array subscript, skip over it and continue. */ + ni = skipsubscript (string, i, 0); + if (string[ni] == ']') + i = ni; + } +#endif + else if (MEMBER (c, charlist)) + { + found = 1; + break; + } + + ADVANCE_CHAR (string, slen, i); + } + + /* If we had to have a matching delimiter and didn't find one, return an + error and let the caller deal with it. */ + if ((flags & SX_REQMATCH) && found == 0) + { + *sindex = i; + return (&extract_string_error); + } + + temp = (flags & SX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i); + *sindex = i; + + return (temp); +} + +/* Extract the contents of STRING as if it is enclosed in double quotes. + SINDEX, when passed in, is the offset of the character immediately + following the opening double quote; on exit, SINDEX is left pointing after + the closing double quote. If STRIPDQ is non-zero, unquoted double + quotes are stripped and the string is terminated by a null byte. + Backslashes between the embedded double quotes are processed. If STRIPDQ + is zero, an unquoted `"' terminates the string. */ +static char * +string_extract_double_quoted (string, sindex, stripdq) + char *string; + int *sindex, stripdq; +{ + size_t slen; + char *send; + int j, i, t; + unsigned char c; + char *temp, *ret; /* The new string we return. */ + int pass_next, backquote, si; /* State variables for the machine. */ + int dquote; + DECLARE_MBSTATE; + + slen = strlen (string + *sindex) + *sindex; + send = string + slen; + + pass_next = backquote = dquote = 0; + temp = (char *)xmalloc (1 + slen - *sindex); + + j = 0; + i = *sindex; + while (c = string[i]) + { + /* Process a character that was quoted by a backslash. */ + if (pass_next) + { + /* XXX - take another look at this in light of Interp 221 */ + /* Posix.2 sez: + + ``The backslash shall retain its special meaning as an escape + character only when followed by one of the characters: + $ ` " \ <newline>''. + + If STRIPDQ is zero, we handle the double quotes here and let + expand_word_internal handle the rest. If STRIPDQ is non-zero, + we have already been through one round of backslash stripping, + and want to strip these backslashes only if DQUOTE is non-zero, + indicating that we are inside an embedded double-quoted string. */ + + /* If we are in an embedded quoted string, then don't strip + backslashes before characters for which the backslash + retains its special meaning, but remove backslashes in + front of other characters. If we are not in an + embedded quoted string, don't strip backslashes at all. + This mess is necessary because the string was already + surrounded by double quotes (and sh has some really weird + quoting rules). + The returned string will be run through expansion as if + it were double-quoted. */ + if ((stripdq == 0 && c != '"') || + (stripdq && ((dquote && (sh_syntaxtab[c] & CBSDQUOTE)) || dquote == 0))) + temp[j++] = '\\'; + pass_next = 0; + +add_one_character: + COPY_CHAR_I (temp, j, string, send, i); + continue; + } + + /* A backslash protects the next character. The code just above + handles preserving the backslash in front of any character but + a double quote. */ + if (c == '\\') + { + pass_next++; + i++; + continue; + } + + /* Inside backquotes, ``the portion of the quoted string from the + initial backquote and the characters up to the next backquote + that is not preceded by a backslash, having escape characters + removed, defines that command''. */ + if (backquote) + { + if (c == '`') + backquote = 0; + temp[j++] = c; + i++; + continue; + } + + if (c == '`') + { + temp[j++] = c; + backquote++; + i++; + continue; + } + + /* Pass everything between `$(' and the matching `)' or a quoted + ${ ... } pair through according to the Posix.2 specification. */ + if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE))) + { + int free_ret = 1; + + si = i + 2; + if (string[i + 1] == LPAREN) + ret = extract_command_subst (string, &si, 0); + else + ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, 0); + + temp[j++] = '$'; + temp[j++] = string[i + 1]; + + /* Just paranoia; ret will not be 0 unless no_longjmp_on_fatal_error + is set. */ + if (ret == 0 && no_longjmp_on_fatal_error) + { + free_ret = 0; + ret = string + i + 2; + } + + for (t = 0; ret[t]; t++, j++) + temp[j] = ret[t]; + temp[j] = string[si]; + + if (string[si]) + { + j++; + i = si + 1; + } + else + i = si; + + if (free_ret) + free (ret); + continue; + } + + /* Add any character but a double quote to the quoted string we're + accumulating. */ + if (c != '"') + goto add_one_character; + + /* c == '"' */ + if (stripdq) + { + dquote ^= 1; + i++; + continue; + } + + break; + } + temp[j] = '\0'; + + /* Point to after the closing quote. */ + if (c) + i++; + *sindex = i; + + return (temp); +} + +/* This should really be another option to string_extract_double_quoted. */ +static int +skip_double_quoted (string, slen, sind) + char *string; + size_t slen; + int sind; +{ + int c, i; + char *ret; + int pass_next, backquote, si; + DECLARE_MBSTATE; + + pass_next = backquote = 0; + i = sind; + while (c = string[i]) + { + if (pass_next) + { + pass_next = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next++; + i++; + continue; + } + else if (backquote) + { + if (c == '`') + backquote = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '`') + { + backquote++; + i++; + continue; + } + else if (c == '$' && ((string[i + 1] == LPAREN) || (string[i + 1] == LBRACE))) + { + si = i + 2; + if (string[i + 1] == LPAREN) + ret = extract_command_subst (string, &si, SX_NOALLOC); + else + ret = extract_dollar_brace_string (string, &si, Q_DOUBLE_QUOTES, SX_NOALLOC); + + i = si + 1; + continue; + } + else if (c != '"') + { + ADVANCE_CHAR (string, slen, i); + continue; + } + else + break; + } + + if (c) + i++; + + return (i); +} + +/* Extract the contents of STRING as if it is enclosed in single quotes. + SINDEX, when passed in, is the offset of the character immediately + following the opening single quote; on exit, SINDEX is left pointing after + the closing single quote. */ +static inline char * +string_extract_single_quoted (string, sindex) + char *string; + int *sindex; +{ + register int i; + size_t slen; + char *t; + DECLARE_MBSTATE; + + /* Don't need slen for ADVANCE_CHAR unless multibyte chars possible. */ + slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 0; + i = *sindex; + while (string[i] && string[i] != '\'') + ADVANCE_CHAR (string, slen, i); + + t = substring (string, *sindex, i); + + if (string[i]) + i++; + *sindex = i; + + return (t); +} + +static inline int +skip_single_quoted (string, slen, sind) + const char *string; + size_t slen; + int sind; +{ + register int c; + DECLARE_MBSTATE; + + c = sind; + while (string[c] && string[c] != '\'') + ADVANCE_CHAR (string, slen, c); + + if (string[c]) + c++; + return c; +} + +/* Just like string_extract, but doesn't hack backslashes or any of + that other stuff. Obeys CTLESC quoting. Used to do splitting on $IFS. */ +static char * +string_extract_verbatim (string, slen, sindex, charlist, flags) + char *string; + size_t slen; + int *sindex; + char *charlist; + int flags; +{ + register int i; +#if defined (HANDLE_MULTIBYTE) + size_t clen; + wchar_t *wcharlist; +#endif + int c; + char *temp; + DECLARE_MBSTATE; + + if (charlist[0] == '\'' && charlist[1] == '\0') + { + temp = string_extract_single_quoted (string, sindex); + --*sindex; /* leave *sindex at separator character */ + return temp; + } + + i = *sindex; +#if 0 + /* See how the MBLEN and ADVANCE_CHAR macros work to understand why we need + this only if MB_CUR_MAX > 1. */ + slen = (MB_CUR_MAX > 1) ? strlen (string + *sindex) + *sindex : 1; +#endif +#if defined (HANDLE_MULTIBYTE) + clen = strlen (charlist); + wcharlist = 0; +#endif + while (c = string[i]) + { +#if defined (HANDLE_MULTIBYTE) + size_t mblength; +#endif + if ((flags & SX_NOCTLESC) == 0 && c == CTLESC) + { + i += 2; + continue; + } + /* Even if flags contains SX_NOCTLESC, we let CTLESC quoting CTLNUL + through, to protect the CTLNULs from later calls to + remove_quoted_nulls. */ + else if ((flags & SX_NOESCCTLNUL) == 0 && c == CTLESC && string[i+1] == CTLNUL) + { + i += 2; + continue; + } + +#if defined (HANDLE_MULTIBYTE) + mblength = MBLEN (string + i, slen - i); + if (mblength > 1) + { + wchar_t wc; + mblength = mbtowc (&wc, string + i, slen - i); + if (MB_INVALIDCH (mblength)) + { + if (MEMBER (c, charlist)) + break; + } + else + { + if (wcharlist == 0) + { + size_t len; + len = mbstowcs (wcharlist, charlist, 0); + if (len == -1) + len = 0; + wcharlist = (wchar_t *)xmalloc (sizeof (wchar_t) * (len + 1)); + mbstowcs (wcharlist, charlist, len + 1); + } + + if (wcschr (wcharlist, wc)) + break; + } + } + else +#endif + if (MEMBER (c, charlist)) + break; + + ADVANCE_CHAR (string, slen, i); + } + +#if defined (HANDLE_MULTIBYTE) + FREE (wcharlist); +#endif + + temp = substring (string, *sindex, i); + *sindex = i; + + return (temp); +} + +/* Extract the $( construct in STRING, and return a new string. + Start extracting at (SINDEX) as if we had just seen "$(". + Make (SINDEX) get the position of the matching ")". ) + XFLAGS is additional flags to pass to other extraction functions. */ +char * +extract_command_subst (string, sindex, xflags) + char *string; + int *sindex; + int xflags; +{ + if (string[*sindex] == LPAREN) + return (extract_delimited_string (string, sindex, "$(", "(", ")", xflags|SX_COMMAND)); /*)*/ + else + { + xflags |= (no_longjmp_on_fatal_error ? SX_NOLONGJMP : 0); + return (xparse_dolparen (string, string+*sindex, sindex, xflags)); + } +} + +/* Extract the $[ construct in STRING, and return a new string. (]) + Start extracting at (SINDEX) as if we had just seen "$[". + Make (SINDEX) get the position of the matching "]". */ +char * +extract_arithmetic_subst (string, sindex) + char *string; + int *sindex; +{ + return (extract_delimited_string (string, sindex, "$[", "[", "]", 0)); /*]*/ +} + +#if defined (PROCESS_SUBSTITUTION) +/* Extract the <( or >( construct in STRING, and return a new string. + Start extracting at (SINDEX) as if we had just seen "<(". + Make (SINDEX) get the position of the matching ")". */ /*))*/ +char * +extract_process_subst (string, starter, sindex) + char *string; + char *starter; + int *sindex; +{ + return (extract_delimited_string (string, sindex, starter, "(", ")", 0)); +} +#endif /* PROCESS_SUBSTITUTION */ + +#if defined (ARRAY_VARS) +/* This can be fooled by unquoted right parens in the passed string. If + each caller verifies that the last character in STRING is a right paren, + we don't even need to call extract_delimited_string. */ +char * +extract_array_assignment_list (string, sindex) + char *string; + int *sindex; +{ + int slen; + char *ret; + + slen = strlen (string); /* ( */ + if (string[slen - 1] == ')') + { + ret = substring (string, *sindex, slen - 1); + *sindex = slen - 1; + return ret; + } + return 0; +} +#endif + +/* Extract and create a new string from the contents of STRING, a + character string delimited with OPENER and CLOSER. SINDEX is + the address of an int describing the current offset in STRING; + it should point to just after the first OPENER found. On exit, + SINDEX gets the position of the last character of the matching CLOSER. + If OPENER is more than a single character, ALT_OPENER, if non-null, + contains a character string that can also match CLOSER and thus + needs to be skipped. */ +static char * +extract_delimited_string (string, sindex, opener, alt_opener, closer, flags) + char *string; + int *sindex; + char *opener, *alt_opener, *closer; + int flags; +{ + int i, c, si; + size_t slen; + char *t, *result; + int pass_character, nesting_level, in_comment; + int len_closer, len_opener, len_alt_opener; + DECLARE_MBSTATE; + + slen = strlen (string + *sindex) + *sindex; + len_opener = STRLEN (opener); + len_alt_opener = STRLEN (alt_opener); + len_closer = STRLEN (closer); + + pass_character = in_comment = 0; + + nesting_level = 1; + i = *sindex; + + while (nesting_level) + { + c = string[i]; + + if (c == 0) + break; + + if (in_comment) + { + if (c == '\n') + in_comment = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + + if (pass_character) /* previous char was backslash */ + { + pass_character = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + + /* Not exactly right yet; should handle shell metacharacters and + multibyte characters, too. See COMMENT_BEGIN define in parse.y */ + if ((flags & SX_COMMAND) && c == '#' && (i == 0 || string[i - 1] == '\n' || shellblank (string[i - 1]))) + { + in_comment = 1; + ADVANCE_CHAR (string, slen, i); + continue; + } + + if (c == CTLESC || c == '\\') + { + pass_character++; + i++; + continue; + } + + /* Process a nested command substitution, but only if we're parsing an + arithmetic substitution. */ + if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LPAREN) + { + si = i + 2; + t = extract_command_subst (string, &si, flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Process a nested OPENER. */ + if (STREQN (string + i, opener, len_opener)) + { + si = i + len_opener; + t = extract_delimited_string (string, &si, opener, alt_opener, closer, flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Process a nested ALT_OPENER */ + if (len_alt_opener && STREQN (string + i, alt_opener, len_alt_opener)) + { + si = i + len_alt_opener; + t = extract_delimited_string (string, &si, alt_opener, alt_opener, closer, flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* If the current substring terminates the delimited string, decrement + the nesting level. */ + if (STREQN (string + i, closer, len_closer)) + { + i += len_closer - 1; /* move to last byte of the closer */ + nesting_level--; + if (nesting_level == 0) + break; + } + + /* Pass old-style command substitution through verbatim. */ + if (c == '`') + { + si = i + 1; + t = string_extract (string, &si, "`", flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Pass single-quoted and double-quoted strings through verbatim. */ + if (c == '\'' || c == '"') + { + si = i + 1; + i = (c == '\'') ? skip_single_quoted (string, slen, si) + : skip_double_quoted (string, slen, si); + continue; + } + + /* move past this character, which was not special. */ + ADVANCE_CHAR (string, slen, i); + } + + if (c == 0 && nesting_level) + { + if (no_longjmp_on_fatal_error == 0) + { + report_error (_("bad substitution: no closing `%s' in %s"), closer, string); + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level (DISCARD); + } + else + { + *sindex = i; + return (char *)NULL; + } + } + + si = i - *sindex - len_closer + 1; + if (flags & SX_NOALLOC) + result = (char *)NULL; + else + { + result = (char *)xmalloc (1 + si); + strncpy (result, string + *sindex, si); + result[si] = '\0'; + } + *sindex = i; + + return (result); +} + +/* Extract a parameter expansion expression within ${ and } from STRING. + Obey the Posix.2 rules for finding the ending `}': count braces while + skipping over enclosed quoted strings and command substitutions. + SINDEX is the address of an int describing the current offset in STRING; + it should point to just after the first `{' found. On exit, SINDEX + gets the position of the matching `}'. QUOTED is non-zero if this + occurs inside double quotes. */ +/* XXX -- this is very similar to extract_delimited_string -- XXX */ +static char * +extract_dollar_brace_string (string, sindex, quoted, flags) + char *string; + int *sindex, quoted, flags; +{ + register int i, c; + size_t slen; + int pass_character, nesting_level, si, dolbrace_state; + char *result, *t; + DECLARE_MBSTATE; + + pass_character = 0; + nesting_level = 1; + slen = strlen (string + *sindex) + *sindex; + + /* The handling of dolbrace_state needs to agree with the code in parse.y: + parse_matched_pair() */ + dolbrace_state = 0; + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + dolbrace_state = (flags & SX_POSIXEXP) ? DOLBRACE_QUOTE : DOLBRACE_PARAM; + + i = *sindex; + while (c = string[i]) + { + if (pass_character) + { + pass_character = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + + /* CTLESCs and backslashes quote the next character. */ + if (c == CTLESC || c == '\\') + { + pass_character++; + i++; + continue; + } + + if (string[i] == '$' && string[i+1] == LBRACE) + { + nesting_level++; + i += 2; + continue; + } + + if (c == RBRACE) + { + nesting_level--; + if (nesting_level == 0) + break; + i++; + continue; + } + + /* Pass the contents of old-style command substitutions through + verbatim. */ + if (c == '`') + { + si = i + 1; + t = string_extract (string, &si, "`", flags|SX_NOALLOC); + i = si + 1; + continue; + } + + /* Pass the contents of new-style command substitutions and + arithmetic substitutions through verbatim. */ + if (string[i] == '$' && string[i+1] == LPAREN) + { + si = i + 2; + t = extract_command_subst (string, &si, flags|SX_NOALLOC); + i = si + 1; + continue; + } + +#if 0 + /* Pass the contents of single-quoted and double-quoted strings + through verbatim. */ + if (c == '\'' || c == '"') + { + si = i + 1; + i = (c == '\'') ? skip_single_quoted (string, slen, si) + : skip_double_quoted (string, slen, si); + /* skip_XXX_quoted leaves index one past close quote */ + continue; + } +#else /* XXX - bash-4.2 */ + /* Pass the contents of double-quoted strings through verbatim. */ + if (c == '"') + { + si = i + 1; + i = skip_double_quoted (string, slen, si); + /* skip_XXX_quoted leaves index one past close quote */ + continue; + } + + if (c == '\'') + { +/*itrace("extract_dollar_brace_string: c == single quote flags = %d quoted = %d dolbrace_state = %d", flags, quoted, dolbrace_state);*/ + if (posixly_correct && shell_compatibility_level > 41 && dolbrace_state != DOLBRACE_QUOTE) + ADVANCE_CHAR (string, slen, i); + else + { + si = i + 1; + i = skip_single_quoted (string, slen, si); + } + + continue; + } +#endif + + /* move past this character, which was not special. */ + ADVANCE_CHAR (string, slen, i); + + /* This logic must agree with parse.y:parse_matched_pair, since they + share the same defines. */ + if (dolbrace_state == DOLBRACE_PARAM && c == '%' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == '#' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == '/' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == '^' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && c == ',' && (i - *sindex) > 1) + dolbrace_state = DOLBRACE_QUOTE; + else if (dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", c) != 0) + dolbrace_state = DOLBRACE_OP; + else if (dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", c) == 0) + dolbrace_state = DOLBRACE_WORD; + } + + if (c == 0 && nesting_level) + { + if (no_longjmp_on_fatal_error == 0) + { /* { */ + report_error (_("bad substitution: no closing `%s' in %s"), "}", string); + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level (DISCARD); + } + else + { + *sindex = i; + return ((char *)NULL); + } + } + + result = (flags & SX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i); + *sindex = i; + + return (result); +} + +/* Remove backslashes which are quoting backquotes from STRING. Modifies + STRING, and returns a pointer to it. */ +char * +de_backslash (string) + char *string; +{ + register size_t slen; + register int i, j, prev_i; + DECLARE_MBSTATE; + + slen = strlen (string); + i = j = 0; + + /* Loop copying string[i] to string[j], i >= j. */ + while (i < slen) + { + if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' || + string[i + 1] == '$')) + i++; + prev_i = i; + ADVANCE_CHAR (string, slen, i); + if (j < prev_i) + do string[j++] = string[prev_i++]; while (prev_i < i); + else + j = i; + } + string[j] = '\0'; + + return (string); +} + +#if 0 +/*UNUSED*/ +/* Replace instances of \! in a string with !. */ +void +unquote_bang (string) + char *string; +{ + register int i, j; + register char *temp; + + temp = (char *)xmalloc (1 + strlen (string)); + + for (i = 0, j = 0; (temp[j] = string[i]); i++, j++) + { + if (string[i] == '\\' && string[i + 1] == '!') + { + temp[j] = '!'; + i++; + } + } + strcpy (string, temp); + free (temp); +} +#endif + +#define CQ_RETURN(x) do { no_longjmp_on_fatal_error = 0; return (x); } while (0) + +/* This function assumes s[i] == open; returns with s[ret] == close; used to + parse array subscripts. FLAGS & 1 means to not attempt to skip over + matched pairs of quotes or backquotes, or skip word expansions; it is + intended to be used after expansion has been performed and during final + assignment parsing (see arrayfunc.c:assign_compound_array_list()). */ +static int +skip_matched_pair (string, start, open, close, flags) + const char *string; + int start, open, close, flags; +{ + int i, pass_next, backq, si, c, count; + size_t slen; + char *temp, *ss; + DECLARE_MBSTATE; + + slen = strlen (string + start) + start; + no_longjmp_on_fatal_error = 1; + + i = start + 1; /* skip over leading bracket */ + count = 1; + pass_next = backq = 0; + ss = (char *)string; + while (c = string[i]) + { + if (pass_next) + { + pass_next = 0; + if (c == 0) + CQ_RETURN(i); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (backq) + { + if (c == '`') + backq = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if ((flags & 1) == 0 && c == '`') + { + backq = 1; + i++; + continue; + } + else if ((flags & 1) == 0 && c == open) + { + count++; + i++; + continue; + } + else if (c == close) + { + count--; + if (count == 0) + break; + i++; + continue; + } + else if ((flags & 1) == 0 && (c == '\'' || c == '"')) + { + i = (c == '\'') ? skip_single_quoted (ss, slen, ++i) + : skip_double_quoted (ss, slen, ++i); + /* no increment, the skip functions increment past the closing quote. */ + } + else if ((flags&1) == 0 && c == '$' && (string[i+1] == LPAREN || string[i+1] == LBRACE)) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + + if (string[i+1] == LPAREN) + temp = extract_delimited_string (ss, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */ + else + temp = extract_dollar_brace_string (ss, &si, 0, SX_NOALLOC); + i = si; + if (string[i] == '\0') /* don't increment i past EOS in loop */ + break; + i++; + continue; + } + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(i); +} + +#if defined (ARRAY_VARS) +int +skipsubscript (string, start, flags) + const char *string; + int start, flags; +{ + return (skip_matched_pair (string, start, '[', ']', flags)); +} +#endif + +/* Skip characters in STRING until we find a character in DELIMS, and return + the index of that character. START is the index into string at which we + begin. This is similar in spirit to strpbrk, but it returns an index into + STRING and takes a starting index. This little piece of code knows quite + a lot of shell syntax. It's very similar to skip_double_quoted and other + functions of that ilk. */ +int +skip_to_delim (string, start, delims, flags) + char *string; + int start; + char *delims; + int flags; +{ + int i, pass_next, backq, si, c, invert, skipquote, skipcmd; + size_t slen; + char *temp; + DECLARE_MBSTATE; + + slen = strlen (string + start) + start; + if (flags & SD_NOJMP) + no_longjmp_on_fatal_error = 1; + invert = (flags & SD_INVERT); + skipcmd = (flags & SD_NOSKIPCMD) == 0; + + i = start; + pass_next = backq = 0; + while (c = string[i]) + { + /* If this is non-zero, we should not let quote characters be delimiters + and the current character is a single or double quote. We should not + test whether or not it's a delimiter until after we skip single- or + double-quoted strings. */ + skipquote = ((flags & SD_NOQUOTEDELIM) && (c == '\'' || c =='"')); + if (pass_next) + { + pass_next = 0; + if (c == 0) + CQ_RETURN(i); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (backq) + { + if (c == '`') + backq = 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '`') + { + backq = 1; + i++; + continue; + } + else if (skipquote == 0 && invert == 0 && member (c, delims)) + break; + else if (c == '\'' || c == '"') + { + i = (c == '\'') ? skip_single_quoted (string, slen, ++i) + : skip_double_quoted (string, slen, ++i); + /* no increment, the skip functions increment past the closing quote. */ + } + else if (c == '$' && ((skipcmd && string[i+1] == LPAREN) || string[i+1] == LBRACE)) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + + if (string[i+1] == LPAREN) + temp = extract_delimited_string (string, &si, "$(", "(", ")", SX_NOALLOC|SX_COMMAND); /* ) */ + else + temp = extract_dollar_brace_string (string, &si, 0, SX_NOALLOC); + i = si; + if (string[i] == '\0') /* don't increment i past EOS in loop */ + break; + i++; + continue; + } +#if defined (PROCESS_SUBSTITUTION) + else if (skipcmd && (c == '<' || c == '>') && string[i+1] == LPAREN) + { + si = i + 2; + if (string[si] == '\0') + CQ_RETURN(si); + temp = extract_process_subst (string, (c == '<') ? "<(" : ">(", &si); + i = si; + if (string[i] == '\0') + break; + i++; + continue; + } +#endif /* PROCESS_SUBSTITUTION */ + else if ((skipquote || invert) && (member (c, delims) == 0)) + break; + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(i); +} + +#if defined (READLINE) +/* Return 1 if the portion of STRING ending at EINDEX is quoted (there is + an unclosed quoted string), or if the character at EINDEX is quoted + by a backslash. NO_LONGJMP_ON_FATAL_ERROR is used to flag that the various + single and double-quoted string parsing functions should not return an + error if there are unclosed quotes or braces. The characters that this + recognizes need to be the same as the contents of + rl_completer_quote_characters. */ + +int +char_is_quoted (string, eindex) + char *string; + int eindex; +{ + int i, pass_next, c; + size_t slen; + DECLARE_MBSTATE; + + slen = strlen (string); + no_longjmp_on_fatal_error = 1; + i = pass_next = 0; + while (i <= eindex) + { + c = string[i]; + + if (pass_next) + { + pass_next = 0; + if (i >= eindex) /* XXX was if (i >= eindex - 1) */ + CQ_RETURN(1); + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (c == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (c == '\'' || c == '"') + { + i = (c == '\'') ? skip_single_quoted (string, slen, ++i) + : skip_double_quoted (string, slen, ++i); + if (i > eindex) + CQ_RETURN(1); + /* no increment, the skip_xxx functions go one past end */ + } + else + ADVANCE_CHAR (string, slen, i); + } + + CQ_RETURN(0); +} + +int +unclosed_pair (string, eindex, openstr) + char *string; + int eindex; + char *openstr; +{ + int i, pass_next, openc, olen; + size_t slen; + DECLARE_MBSTATE; + + slen = strlen (string); + olen = strlen (openstr); + i = pass_next = openc = 0; + while (i <= eindex) + { + if (pass_next) + { + pass_next = 0; + if (i >= eindex) /* XXX was if (i >= eindex - 1) */ + return 0; + ADVANCE_CHAR (string, slen, i); + continue; + } + else if (string[i] == '\\') + { + pass_next = 1; + i++; + continue; + } + else if (STREQN (string + i, openstr, olen)) + { + openc = 1 - openc; + i += olen; + } + else if (string[i] == '\'' || string[i] == '"') + { + i = (string[i] == '\'') ? skip_single_quoted (string, slen, i) + : skip_double_quoted (string, slen, i); + if (i > eindex) + return 0; + } + else + ADVANCE_CHAR (string, slen, i); + } + return (openc); +} + +/* Split STRING (length SLEN) at DELIMS, and return a WORD_LIST with the + individual words. If DELIMS is NULL, the current value of $IFS is used + to split the string, and the function follows the shell field splitting + rules. SENTINEL is an index to look for. NWP, if non-NULL, + gets the number of words in the returned list. CWP, if non-NULL, gets + the index of the word containing SENTINEL. Non-whitespace chars in + DELIMS delimit separate fields. */ +WORD_LIST * +split_at_delims (string, slen, delims, sentinel, flags, nwp, cwp) + char *string; + int slen; + char *delims; + int sentinel, flags; + int *nwp, *cwp; +{ + int ts, te, i, nw, cw, ifs_split, dflags; + char *token, *d, *d2; + WORD_LIST *ret, *tl; + + if (string == 0 || *string == '\0') + { + if (nwp) + *nwp = 0; + if (cwp) + *cwp = 0; + return ((WORD_LIST *)NULL); + } + + d = (delims == 0) ? ifs_value : delims; + ifs_split = delims == 0; + + /* Make d2 the non-whitespace characters in delims */ + d2 = 0; + if (delims) + { + size_t slength; +#if defined (HANDLE_MULTIBYTE) + size_t mblength = 1; +#endif + DECLARE_MBSTATE; + + slength = strlen (delims); + d2 = (char *)xmalloc (slength + 1); + i = ts = 0; + while (delims[i]) + { +#if defined (HANDLE_MULTIBYTE) + mbstate_t state_bak; + state_bak = state; + mblength = MBRLEN (delims + i, slength, &state); + if (MB_INVALIDCH (mblength)) + state = state_bak; + else if (mblength > 1) + { + memcpy (d2 + ts, delims + i, mblength); + ts += mblength; + i += mblength; + slength -= mblength; + continue; + } +#endif + if (whitespace (delims[i]) == 0) + d2[ts++] = delims[i]; + + i++; + slength--; + } + d2[ts] = '\0'; + } + + ret = (WORD_LIST *)NULL; + + /* Remove sequences of whitespace characters at the start of the string, as + long as those characters are delimiters. */ + for (i = 0; member (string[i], d) && spctabnl (string[i]); i++) + ; + if (string[i] == '\0') + return (ret); + + ts = i; + nw = 0; + cw = -1; + dflags = flags|SD_NOJMP; + while (1) + { + te = skip_to_delim (string, ts, d, dflags); + + /* If we have a non-whitespace delimiter character, use it to make a + separate field. This is just about what $IFS splitting does and + is closer to the behavior of the shell parser. */ + if (ts == te && d2 && member (string[ts], d2)) + { + te = ts + 1; + /* If we're using IFS splitting, the non-whitespace delimiter char + and any additional IFS whitespace delimits a field. */ + if (ifs_split) + while (member (string[te], d) && spctabnl (string[te])) + te++; + else + while (member (string[te], d2)) + te++; + } + + token = substring (string, ts, te); + + ret = add_string_to_list (token, ret); + free (token); + nw++; + + if (sentinel >= ts && sentinel <= te) + cw = nw; + + /* If the cursor is at whitespace just before word start, set the + sentinel word to the current word. */ + if (cwp && cw == -1 && sentinel == ts-1) + cw = nw; + + /* If the cursor is at whitespace between two words, make a new, empty + word, add it before (well, after, since the list is in reverse order) + the word we just added, and set the current word to that one. */ + if (cwp && cw == -1 && sentinel < ts) + { + tl = make_word_list (make_word (""), ret->next); + ret->next = tl; + cw = nw; + nw++; + } + + if (string[te] == 0) + break; + + i = te; + while (member (string[i], d) && (ifs_split || spctabnl(string[i]))) + i++; + + if (string[i]) + ts = i; + else + break; + } + + /* Special case for SENTINEL at the end of STRING. If we haven't found + the word containing SENTINEL yet, and the index we're looking for is at + the end of STRING (or past the end of the previously-found token, + possible if the end of the line is composed solely of IFS whitespace) + add an additional null argument and set the current word pointer to that. */ + if (cwp && cw == -1 && (sentinel >= slen || sentinel >= te)) + { + if (whitespace (string[sentinel - 1])) + { + token = ""; + ret = add_string_to_list (token, ret); + nw++; + } + cw = nw; + } + + if (nwp) + *nwp = nw; + if (cwp) + *cwp = cw; + + return (REVERSE_LIST (ret, WORD_LIST *)); +} +#endif /* READLINE */ + +#if 0 +/* UNUSED */ +/* Extract the name of the variable to bind to from the assignment string. */ +char * +assignment_name (string) + char *string; +{ + int offset; + char *temp; + + offset = assignment (string, 0); + if (offset == 0) + return (char *)NULL; + temp = substring (string, 0, offset); + return (temp); +} +#endif + +/* **************************************************************** */ +/* */ +/* Functions to convert strings to WORD_LISTs and vice versa */ +/* */ +/* **************************************************************** */ + +/* Return a single string of all the words in LIST. SEP is the separator + to put between individual elements of LIST in the output string. */ +char * +string_list_internal (list, sep) + WORD_LIST *list; + char *sep; +{ + register WORD_LIST *t; + char *result, *r; + int word_len, sep_len, result_size; + + if (list == 0) + return ((char *)NULL); + + /* Short-circuit quickly if we don't need to separate anything. */ + if (list->next == 0) + return (savestring (list->word->word)); + + /* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */ + sep_len = STRLEN (sep); + result_size = 0; + + for (t = list; t; t = t->next) + { + if (t != list) + result_size += sep_len; + result_size += strlen (t->word->word); + } + + r = result = (char *)xmalloc (result_size + 1); + + for (t = list; t; t = t->next) + { + if (t != list && sep_len) + { + if (sep_len > 1) + { + FASTCOPY (sep, r, sep_len); + r += sep_len; + } + else + *r++ = sep[0]; + } + + word_len = strlen (t->word->word); + FASTCOPY (t->word->word, r, word_len); + r += word_len; + } + + *r = '\0'; + return (result); +} + +/* Return a single string of all the words present in LIST, separating + each word with a space. */ +char * +string_list (list) + WORD_LIST *list; +{ + return (string_list_internal (list, " ")); +} + +/* An external interface that can be used by the rest of the shell to + obtain a string containing the first character in $IFS. Handles all + the multibyte complications. If LENP is non-null, it is set to the + length of the returned string. */ +char * +ifs_firstchar (lenp) + int *lenp; +{ + char *ret; + int len; + + ret = xmalloc (MB_LEN_MAX + 1); +#if defined (HANDLE_MULTIBYTE) + if (ifs_firstc_len == 1) + { + ret[0] = ifs_firstc[0]; + ret[1] = '\0'; + len = ret[0] ? 1 : 0; + } + else + { + memcpy (ret, ifs_firstc, ifs_firstc_len); + ret[len = ifs_firstc_len] = '\0'; + } +#else + ret[0] = ifs_firstc; + ret[1] = '\0'; + len = ret[0] ? 0 : 1; +#endif + + if (lenp) + *lenp = len; + + return ret; +} + +/* Return a single string of all the words present in LIST, obeying the + quoting rules for "$*", to wit: (P1003.2, draft 11, 3.5.2) "If the + expansion [of $*] appears within a double quoted string, it expands + to a single field with the value of each parameter separated by the + first character of the IFS variable, or by a <space> if IFS is unset." */ +char * +string_list_dollar_star (list) + WORD_LIST *list; +{ + char *ret; +#if defined (HANDLE_MULTIBYTE) +# if defined (__GNUC__) + char sep[MB_CUR_MAX + 1]; +# else + char *sep = 0; +# endif +#else + char sep[2]; +#endif + +#if defined (HANDLE_MULTIBYTE) +# if !defined (__GNUC__) + sep = (char *)xmalloc (MB_CUR_MAX + 1); +# endif /* !__GNUC__ */ + if (ifs_firstc_len == 1) + { + sep[0] = ifs_firstc[0]; + sep[1] = '\0'; + } + else + { + memcpy (sep, ifs_firstc, ifs_firstc_len); + sep[ifs_firstc_len] = '\0'; + } +#else + sep[0] = ifs_firstc; + sep[1] = '\0'; +#endif + + ret = string_list_internal (list, sep); +#if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__) + free (sep); +#endif + return ret; +} + +/* Turn $@ into a string. If (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + is non-zero, the $@ appears within double quotes, and we should quote + the list before converting it into a string. If IFS is unset, and the + word is not quoted, we just need to quote CTLESC and CTLNUL characters + in the words in the list, because the default value of $IFS is + <space><tab><newline>, IFS characters in the words in the list should + also be split. If IFS is null, and the word is not quoted, we need + to quote the words in the list to preserve the positional parameters + exactly. */ +char * +string_list_dollar_at (list, quoted) + WORD_LIST *list; + int quoted; +{ + char *ifs, *ret; +#if defined (HANDLE_MULTIBYTE) +# if defined (__GNUC__) + char sep[MB_CUR_MAX + 1]; +# else + char *sep = 0; +# endif /* !__GNUC__ */ +#else + char sep[2]; +#endif + WORD_LIST *tlist; + + /* XXX this could just be ifs = ifs_value; */ + ifs = ifs_var ? value_cell (ifs_var) : (char *)0; + +#if defined (HANDLE_MULTIBYTE) +# if !defined (__GNUC__) + sep = (char *)xmalloc (MB_CUR_MAX + 1); +# endif /* !__GNUC__ */ + if (ifs && *ifs) + { + if (ifs_firstc_len == 1) + { + sep[0] = ifs_firstc[0]; + sep[1] = '\0'; + } + else + { + memcpy (sep, ifs_firstc, ifs_firstc_len); + sep[ifs_firstc_len] = '\0'; + } + } + else + { + sep[0] = ' '; + sep[1] = '\0'; + } +#else + sep[0] = (ifs == 0 || *ifs == 0) ? ' ' : *ifs; + sep[1] = '\0'; +#endif + + /* XXX -- why call quote_list if ifs == 0? we can get away without doing + it now that quote_escapes quotes spaces */ +#if 0 + tlist = ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (ifs && *ifs == 0)) +#else + tlist = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE)) +#endif + ? quote_list (list) + : list_quote_escapes (list); + + ret = string_list_internal (tlist, sep); +#if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__) + free (sep); +#endif + return ret; +} + +/* Turn the positional paramters into a string, understanding quoting and + the various subtleties of using the first character of $IFS as the + separator. Calls string_list_dollar_at, string_list_dollar_star, and + string_list as appropriate. */ +char * +string_list_pos_params (pchar, list, quoted) + int pchar; + WORD_LIST *list; + int quoted; +{ + char *ret; + WORD_LIST *tlist; + + if (pchar == '*' && (quoted & Q_DOUBLE_QUOTES)) + { + tlist = quote_list (list); + word_list_remove_quoted_nulls (tlist); + ret = string_list_dollar_star (tlist); + } + else if (pchar == '*' && (quoted & Q_HERE_DOCUMENT)) + { + tlist = quote_list (list); + word_list_remove_quoted_nulls (tlist); + ret = string_list (tlist); + } + else if (pchar == '*') + { + /* Even when unquoted, string_list_dollar_star does the right thing + making sure that the first character of $IFS is used as the + separator. */ + ret = string_list_dollar_star (list); + } + else if (pchar == '@' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + /* We use string_list_dollar_at, but only if the string is quoted, since + that quotes the escapes if it's not, which we don't want. We could + use string_list (the old code did), but that doesn't do the right + thing if the first character of $IFS is not a space. We use + string_list_dollar_star if the string is unquoted so we make sure that + the elements of $@ are separated by the first character of $IFS for + later splitting. */ + ret = string_list_dollar_at (list, quoted); + else if (pchar == '@') + ret = string_list_dollar_star (list); + else + ret = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (list) : list); + + return ret; +} + +/* Return the list of words present in STRING. Separate the string into + words at any of the characters found in SEPARATORS. If QUOTED is + non-zero then word in the list will have its quoted flag set, otherwise + the quoted flag is left as make_word () deemed fit. + + This obeys the P1003.2 word splitting semantics. If `separators' is + exactly <space><tab><newline>, then the splitting algorithm is that of + the Bourne shell, which treats any sequence of characters from `separators' + as a delimiter. If IFS is unset, which results in `separators' being set + to "", no splitting occurs. If separators has some other value, the + following rules are applied (`IFS white space' means zero or more + occurrences of <space>, <tab>, or <newline>, as long as those characters + are in `separators'): + + 1) IFS white space is ignored at the start and the end of the + string. + 2) Each occurrence of a character in `separators' that is not + IFS white space, along with any adjacent occurrences of + IFS white space delimits a field. + 3) Any nonzero-length sequence of IFS white space delimits a field. + */ + +/* BEWARE! list_string strips null arguments. Don't call it twice and + expect to have "" preserved! */ + +/* This performs word splitting and quoted null character removal on + STRING. */ +#define issep(c) \ + (((separators)[0]) ? ((separators)[1] ? isifs(c) \ + : (c) == (separators)[0]) \ + : 0) + +WORD_LIST * +list_string (string, separators, quoted) + register char *string, *separators; + int quoted; +{ + WORD_LIST *result; + WORD_DESC *t; + char *current_word, *s; + int sindex, sh_style_split, whitesep, xflags; + size_t slen; + + if (!string || !*string) + return ((WORD_LIST *)NULL); + + sh_style_split = separators && separators[0] == ' ' && + separators[1] == '\t' && + separators[2] == '\n' && + separators[3] == '\0'; + for (xflags = 0, s = ifs_value; s && *s; s++) + { + if (*s == CTLESC) xflags |= SX_NOCTLESC; + else if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL; + } + + slen = 0; + /* Remove sequences of whitespace at the beginning of STRING, as + long as those characters appear in IFS. Do not do this if + STRING is quoted or if there are no separator characters. */ + if (!quoted || !separators || !*separators) + { + for (s = string; *s && spctabnl (*s) && issep (*s); s++); + + if (!*s) + return ((WORD_LIST *)NULL); + + string = s; + } + + /* OK, now STRING points to a word that does not begin with white space. + The splitting algorithm is: + extract a word, stopping at a separator + skip sequences of spc, tab, or nl as long as they are separators + This obeys the field splitting rules in Posix.2. */ + slen = (MB_CUR_MAX > 1) ? strlen (string) : 1; + for (result = (WORD_LIST *)NULL, sindex = 0; string[sindex]; ) + { + /* Don't need string length in ADVANCE_CHAR or string_extract_verbatim + unless multibyte chars are possible. */ + current_word = string_extract_verbatim (string, slen, &sindex, separators, xflags); + if (current_word == 0) + break; + + /* If we have a quoted empty string, add a quoted null argument. We + want to preserve the quoted null character iff this is a quoted + empty string; otherwise the quoted null characters are removed + below. */ + if (QUOTED_NULL (current_word)) + { + t = alloc_word_desc (); + t->word = make_quoted_char ('\0'); + t->flags |= W_QUOTED|W_HASQUOTEDNULL; + result = make_word_list (t, result); + } + else if (current_word[0] != '\0') + { + /* If we have something, then add it regardless. However, + perform quoted null character removal on the current word. */ + remove_quoted_nulls (current_word); + result = add_string_to_list (current_word, result); + result->word->flags &= ~W_HASQUOTEDNULL; /* just to be sure */ + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + result->word->flags |= W_QUOTED; + } + + /* If we're not doing sequences of separators in the traditional + Bourne shell style, then add a quoted null argument. */ + else if (!sh_style_split && !spctabnl (string[sindex])) + { + t = alloc_word_desc (); + t->word = make_quoted_char ('\0'); + t->flags |= W_QUOTED|W_HASQUOTEDNULL; + result = make_word_list (t, result); + } + + free (current_word); + + /* Note whether or not the separator is IFS whitespace, used later. */ + whitesep = string[sindex] && spctabnl (string[sindex]); + + /* Move past the current separator character. */ + if (string[sindex]) + { + DECLARE_MBSTATE; + ADVANCE_CHAR (string, slen, sindex); + } + + /* Now skip sequences of space, tab, or newline characters if they are + in the list of separators. */ + while (string[sindex] && spctabnl (string[sindex]) && issep (string[sindex])) + sindex++; + + /* If the first separator was IFS whitespace and the current character + is a non-whitespace IFS character, it should be part of the current + field delimiter, not a separate delimiter that would result in an + empty field. Look at POSIX.2, 3.6.5, (3)(b). */ + if (string[sindex] && whitesep && issep (string[sindex]) && !spctabnl (string[sindex])) + { + sindex++; + /* An IFS character that is not IFS white space, along with any + adjacent IFS white space, shall delimit a field. (SUSv3) */ + while (string[sindex] && spctabnl (string[sindex]) && isifs (string[sindex])) + sindex++; + } + } + return (REVERSE_LIST (result, WORD_LIST *)); +} + +/* Parse a single word from STRING, using SEPARATORS to separate fields. + ENDPTR is set to the first character after the word. This is used by + the `read' builtin. This is never called with SEPARATORS != $IFS; + it should be simplified. + + XXX - this function is very similar to list_string; they should be + combined - XXX */ +char * +get_word_from_string (stringp, separators, endptr) + char **stringp, *separators, **endptr; +{ + register char *s; + char *current_word; + int sindex, sh_style_split, whitesep, xflags; + size_t slen; + + if (!stringp || !*stringp || !**stringp) + return ((char *)NULL); + + sh_style_split = separators && separators[0] == ' ' && + separators[1] == '\t' && + separators[2] == '\n' && + separators[3] == '\0'; + for (xflags = 0, s = ifs_value; s && *s; s++) + { + if (*s == CTLESC) xflags |= SX_NOCTLESC; + if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL; + } + + s = *stringp; + slen = 0; + + /* Remove sequences of whitespace at the beginning of STRING, as + long as those characters appear in IFS. */ + if (sh_style_split || !separators || !*separators) + { + for (; *s && spctabnl (*s) && isifs (*s); s++); + + /* If the string is nothing but whitespace, update it and return. */ + if (!*s) + { + *stringp = s; + if (endptr) + *endptr = s; + return ((char *)NULL); + } + } + + /* OK, S points to a word that does not begin with white space. + Now extract a word, stopping at a separator, save a pointer to + the first character after the word, then skip sequences of spc, + tab, or nl as long as they are separators. + + This obeys the field splitting rules in Posix.2. */ + sindex = 0; + /* Don't need string length in ADVANCE_CHAR or string_extract_verbatim + unless multibyte chars are possible. */ + slen = (MB_CUR_MAX > 1) ? strlen (s) : 1; + current_word = string_extract_verbatim (s, slen, &sindex, separators, xflags); + + /* Set ENDPTR to the first character after the end of the word. */ + if (endptr) + *endptr = s + sindex; + + /* Note whether or not the separator is IFS whitespace, used later. */ + whitesep = s[sindex] && spctabnl (s[sindex]); + + /* Move past the current separator character. */ + if (s[sindex]) + { + DECLARE_MBSTATE; + ADVANCE_CHAR (s, slen, sindex); + } + + /* Now skip sequences of space, tab, or newline characters if they are + in the list of separators. */ + while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex])) + sindex++; + + /* If the first separator was IFS whitespace and the current character is + a non-whitespace IFS character, it should be part of the current field + delimiter, not a separate delimiter that would result in an empty field. + Look at POSIX.2, 3.6.5, (3)(b). */ + if (s[sindex] && whitesep && isifs (s[sindex]) && !spctabnl (s[sindex])) + { + sindex++; + /* An IFS character that is not IFS white space, along with any adjacent + IFS white space, shall delimit a field. */ + while (s[sindex] && spctabnl (s[sindex]) && isifs (s[sindex])) + sindex++; + } + + /* Update STRING to point to the next field. */ + *stringp = s + sindex; + return (current_word); +} + +/* Remove IFS white space at the end of STRING. Start at the end + of the string and walk backwards until the beginning of the string + or we find a character that's not IFS white space and not CTLESC. + Only let CTLESC escape a white space character if SAW_ESCAPE is + non-zero. */ +char * +strip_trailing_ifs_whitespace (string, separators, saw_escape) + char *string, *separators; + int saw_escape; +{ + char *s; + + s = string + STRLEN (string) - 1; + while (s > string && ((spctabnl (*s) && isifs (*s)) || + (saw_escape && *s == CTLESC && spctabnl (s[1])))) + s--; + *++s = '\0'; + return string; +} + +#if 0 +/* UNUSED */ +/* Split STRING into words at whitespace. Obeys shell-style quoting with + backslashes, single and double quotes. */ +WORD_LIST * +list_string_with_quotes (string) + char *string; +{ + WORD_LIST *list; + char *token, *s; + size_t s_len; + int c, i, tokstart, len; + + for (s = string; s && *s && spctabnl (*s); s++) + ; + if (s == 0 || *s == 0) + return ((WORD_LIST *)NULL); + + s_len = strlen (s); + tokstart = i = 0; + list = (WORD_LIST *)NULL; + while (1) + { + c = s[i]; + if (c == '\\') + { + i++; + if (s[i]) + i++; + } + else if (c == '\'') + i = skip_single_quoted (s, s_len, ++i); + else if (c == '"') + i = skip_double_quoted (s, s_len, ++i); + else if (c == 0 || spctabnl (c)) + { + /* We have found the end of a token. Make a word out of it and + add it to the word list. */ + token = substring (s, tokstart, i); + list = add_string_to_list (token, list); + free (token); + while (spctabnl (s[i])) + i++; + if (s[i]) + tokstart = i; + else + break; + } + else + i++; /* normal character */ + } + return (REVERSE_LIST (list, WORD_LIST *)); +} +#endif + +/********************************************************/ +/* */ +/* Functions to perform assignment statements */ +/* */ +/********************************************************/ + +#if defined (ARRAY_VARS) +static SHELL_VAR * +do_compound_assignment (name, value, flags) + char *name, *value; + int flags; +{ + SHELL_VAR *v; + int mklocal, mkassoc; + WORD_LIST *list; + + mklocal = flags & ASS_MKLOCAL; + mkassoc = flags & ASS_MKASSOC; + + if (mklocal && variable_context) + { + v = find_variable (name); + list = expand_compound_array_assignment (v, value, flags); + if (mkassoc) + v = make_local_assoc_variable (name); + else if (v == 0 || (array_p (v) == 0 && assoc_p (v) == 0) || v->context != variable_context) + v = make_local_array_variable (name); + assign_compound_array_list (v, list, flags); + } + else + v = assign_array_from_string (name, value, flags); + + return (v); +} +#endif + +/* Given STRING, an assignment string, get the value of the right side + of the `=', and bind it to the left side. If EXPAND is true, then + perform parameter expansion, command substitution, and arithmetic + expansion on the right-hand side. Perform tilde expansion in any + case. Do not perform word splitting on the result of expansion. */ +static int +do_assignment_internal (word, expand) + const WORD_DESC *word; + int expand; +{ + int offset, appendop, assign_list, aflags, retval; + char *name, *value, *temp; + SHELL_VAR *entry; +#if defined (ARRAY_VARS) + char *t; + int ni; +#endif + const char *string; + + if (word == 0 || word->word == 0) + return 0; + + appendop = assign_list = aflags = 0; + string = word->word; + offset = assignment (string, 0); + name = savestring (string); + value = (char *)NULL; + + if (name[offset] == '=') + { + if (name[offset - 1] == '+') + { + appendop = 1; + name[offset - 1] = '\0'; + } + + name[offset] = 0; /* might need this set later */ + temp = name + offset + 1; + +#if defined (ARRAY_VARS) + if (expand && (word->flags & W_COMPASSIGN)) + { + assign_list = ni = 1; + value = extract_array_assignment_list (temp, &ni); + } + else +#endif + if (expand && temp[0]) + value = expand_string_if_necessary (temp, 0, expand_string_assignment); + else + value = savestring (temp); + } + + if (value == 0) + { + value = (char *)xmalloc (1); + value[0] = '\0'; + } + + if (echo_command_at_execute) + { + if (appendop) + name[offset - 1] = '+'; + xtrace_print_assignment (name, value, assign_list, 1); + if (appendop) + name[offset - 1] = '\0'; + } + +#define ASSIGN_RETURN(r) do { FREE (value); free (name); return (r); } while (0) + + if (appendop) + aflags |= ASS_APPEND; + +#if defined (ARRAY_VARS) + if (t = mbschr (name, '[')) /*]*/ + { + if (assign_list) + { + report_error (_("%s: cannot assign list to array member"), name); + ASSIGN_RETURN (0); + } + entry = assign_array_element (name, value, aflags); + if (entry == 0) + ASSIGN_RETURN (0); + } + else if (assign_list) + { + if (word->flags & W_ASSIGNARG) + aflags |= ASS_MKLOCAL; + if (word->flags & W_ASSIGNASSOC) + aflags |= ASS_MKASSOC; + entry = do_compound_assignment (name, value, aflags); + } + else +#endif /* ARRAY_VARS */ + entry = bind_variable (name, value, aflags); + + stupidly_hack_special_variables (name); + +#if 1 + /* Return 1 if the assignment seems to have been performed correctly. */ + if (entry == 0 || readonly_p (entry)) + retval = 0; /* assignment failure */ + else if (noassign_p (entry)) + { + last_command_exit_value = EXECUTION_FAILURE; + retval = 1; /* error status, but not assignment failure */ + } + else + retval = 1; + + if (entry && retval != 0 && noassign_p (entry) == 0) + VUNSETATTR (entry, att_invisible); + + ASSIGN_RETURN (retval); +#else + if (entry) + VUNSETATTR (entry, att_invisible); + + ASSIGN_RETURN (entry ? ((readonly_p (entry) == 0) && noassign_p (entry) == 0) : 0); +#endif +} + +/* Perform the assignment statement in STRING, and expand the + right side by doing tilde, command and parameter expansion. */ +int +do_assignment (string) + char *string; +{ + WORD_DESC td; + + td.flags = W_ASSIGNMENT; + td.word = string; + + return do_assignment_internal (&td, 1); +} + +int +do_word_assignment (word) + WORD_DESC *word; +{ + return do_assignment_internal (word, 1); +} + +/* Given STRING, an assignment string, get the value of the right side + of the `=', and bind it to the left side. Do not perform any word + expansions on the right hand side. */ +int +do_assignment_no_expand (string) + char *string; +{ + WORD_DESC td; + + td.flags = W_ASSIGNMENT; + td.word = string; + + return (do_assignment_internal (&td, 0)); +} + +/*************************************************** + * * + * Functions to manage the positional parameters * + * * + ***************************************************/ + +/* Return the word list that corresponds to `$*'. */ +WORD_LIST * +list_rest_of_args () +{ + register WORD_LIST *list, *args; + int i; + + /* Break out of the loop as soon as one of the dollar variables is null. */ + for (i = 1, list = (WORD_LIST *)NULL; i < 10 && dollar_vars[i]; i++) + list = make_word_list (make_bare_word (dollar_vars[i]), list); + + for (args = rest_of_args; args; args = args->next) + list = make_word_list (make_bare_word (args->word->word), list); + + return (REVERSE_LIST (list, WORD_LIST *)); +} + +int +number_of_args () +{ + register WORD_LIST *list; + int n; + + for (n = 0; n < 9 && dollar_vars[n+1]; n++) + ; + for (list = rest_of_args; list; list = list->next) + n++; + return n; +} + +/* Return the value of a positional parameter. This handles values > 10. */ +char * +get_dollar_var_value (ind) + intmax_t ind; +{ + char *temp; + WORD_LIST *p; + + if (ind < 10) + temp = dollar_vars[ind] ? savestring (dollar_vars[ind]) : (char *)NULL; + else /* We want something like ${11} */ + { + ind -= 10; + for (p = rest_of_args; p && ind--; p = p->next) + ; + temp = p ? savestring (p->word->word) : (char *)NULL; + } + return (temp); +} + +/* Make a single large string out of the dollar digit variables, + and the rest_of_args. If DOLLAR_STAR is 1, then obey the special + case of "$*" with respect to IFS. */ +char * +string_rest_of_args (dollar_star) + int dollar_star; +{ + register WORD_LIST *list; + char *string; + + list = list_rest_of_args (); + string = dollar_star ? string_list_dollar_star (list) : string_list (list); + dispose_words (list); + return (string); +} + +/* Return a string containing the positional parameters from START to + END, inclusive. If STRING[0] == '*', we obey the rules for $*, + which only makes a difference if QUOTED is non-zero. If QUOTED includes + Q_HERE_DOCUMENT or Q_DOUBLE_QUOTES, this returns a quoted list, otherwise + no quoting chars are added. */ +static char * +pos_params (string, start, end, quoted) + char *string; + int start, end, quoted; +{ + WORD_LIST *save, *params, *h, *t; + char *ret; + int i; + + /* see if we can short-circuit. if start == end, we want 0 parameters. */ + if (start == end) + return ((char *)NULL); + + save = params = list_rest_of_args (); + if (save == 0) + return ((char *)NULL); + + if (start == 0) /* handle ${@:0[:x]} specially */ + { + t = make_word_list (make_word (dollar_vars[0]), params); + save = params = t; + } + + for (i = start ? 1 : 0; params && i < start; i++) + params = params->next; + if (params == 0) + return ((char *)NULL); + for (h = t = params; params && i < end; i++) + { + t = params; + params = params->next; + } + + t->next = (WORD_LIST *)NULL; + + ret = string_list_pos_params (string[0], h, quoted); + + if (t != params) + t->next = params; + + dispose_words (save); + return (ret); +} + +/******************************************************************/ +/* */ +/* Functions to expand strings to strings or WORD_LISTs */ +/* */ +/******************************************************************/ + +#if defined (PROCESS_SUBSTITUTION) +#define EXP_CHAR(s) (s == '$' || s == '`' || s == '<' || s == '>' || s == CTLESC || s == '~') +#else +#define EXP_CHAR(s) (s == '$' || s == '`' || s == CTLESC || s == '~') +#endif + +/* If there are any characters in STRING that require full expansion, + then call FUNC to expand STRING; otherwise just perform quote + removal if necessary. This returns a new string. */ +static char * +expand_string_if_necessary (string, quoted, func) + char *string; + int quoted; + EXPFUNC *func; +{ + WORD_LIST *list; + size_t slen; + int i, saw_quote; + char *ret; + DECLARE_MBSTATE; + + /* Don't need string length for ADVANCE_CHAR unless multibyte chars possible. */ + slen = (MB_CUR_MAX > 1) ? strlen (string) : 0; + i = saw_quote = 0; + while (string[i]) + { + if (EXP_CHAR (string[i])) + break; + else if (string[i] == '\'' || string[i] == '\\' || string[i] == '"') + saw_quote = 1; + ADVANCE_CHAR (string, slen, i); + } + + if (string[i]) + { + list = (*func) (string, quoted); + if (list) + { + ret = string_list (list); + dispose_words (list); + } + else + ret = (char *)NULL; + } + else if (saw_quote && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)) + ret = string_quote_removal (string, quoted); + else + ret = savestring (string); + + return ret; +} + +static inline char * +expand_string_to_string_internal (string, quoted, func) + char *string; + int quoted; + EXPFUNC *func; +{ + WORD_LIST *list; + char *ret; + + if (string == 0 || *string == '\0') + return ((char *)NULL); + + list = (*func) (string, quoted); + if (list) + { + ret = string_list (list); + dispose_words (list); + } + else + ret = (char *)NULL; + + return (ret); +} + +char * +expand_string_to_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_to_string_internal (string, quoted, expand_string)); +} + +char * +expand_string_unsplit_to_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_to_string_internal (string, quoted, expand_string_unsplit)); +} + +char * +expand_assignment_string_to_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_to_string_internal (string, quoted, expand_string_assignment)); +} + +char * +expand_arith_string (string, quoted) + char *string; + int quoted; +{ + return (expand_string_if_necessary (string, quoted, expand_string)); +} + +#if defined (COND_COMMAND) +/* Just remove backslashes in STRING. Returns a new string. */ +char * +remove_backslashes (string) + char *string; +{ + char *r, *ret, *s; + + r = ret = (char *)xmalloc (strlen (string) + 1); + for (s = string; s && *s; ) + { + if (*s == '\\') + s++; + if (*s == 0) + break; + *r++ = *s++; + } + *r = '\0'; + return ret; +} + +/* This needs better error handling. */ +/* Expand W for use as an argument to a unary or binary operator in a + [[...]] expression. If SPECIAL is 1, this is the rhs argument + to the != or == operator, and should be treated as a pattern. In + this case, we quote the string specially for the globbing code. If + SPECIAL is 2, this is an rhs argument for the =~ operator, and should + be quoted appropriately for regcomp/regexec. The caller is responsible + for removing the backslashes if the unquoted word is needed later. */ +char * +cond_expand_word (w, special) + WORD_DESC *w; + int special; +{ + char *r, *p; + WORD_LIST *l; + int qflags; + + if (w->word == 0 || w->word[0] == '\0') + return ((char *)NULL); + + w->flags |= W_NOSPLIT2; + l = call_expand_word_internal (w, 0, 0, (int *)0, (int *)0); + if (l) + { + if (special == 0) + { + dequote_list (l); + r = string_list (l); + } + else + { + qflags = QGLOB_CVTNULL; + if (special == 2) + qflags |= QGLOB_REGEXP; + p = string_list (l); + r = quote_string_for_globbing (p, qflags); + free (p); + } + dispose_words (l); + } + else + r = (char *)NULL; + + return r; +} +#endif + +/* Call expand_word_internal to expand W and handle error returns. + A convenience function for functions that don't want to handle + any errors or free any memory before aborting. */ +static WORD_LIST * +call_expand_word_internal (w, q, i, c, e) + WORD_DESC *w; + int q, i, *c, *e; +{ + WORD_LIST *result; + + result = expand_word_internal (w, q, i, c, e); + if (result == &expand_word_error || result == &expand_word_fatal) + { + /* By convention, each time this error is returned, w->word has + already been freed (it sometimes may not be in the fatal case, + but that doesn't result in a memory leak because we're going + to exit in most cases). */ + w->word = (char *)NULL; + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level ((result == &expand_word_error) ? DISCARD : FORCE_EOF); + /* NOTREACHED */ + } + else + return (result); +} + +/* Perform parameter expansion, command substitution, and arithmetic + expansion on STRING, as if it were a word. Leave the result quoted. */ +static WORD_LIST * +expand_string_internal (string, quoted) + char *string; + int quoted; +{ + WORD_DESC td; + WORD_LIST *tresult; + + if (string == 0 || *string == 0) + return ((WORD_LIST *)NULL); + + td.flags = 0; + td.word = savestring (string); + + tresult = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL); + + FREE (td.word); + return (tresult); +} + +/* Expand STRING by performing parameter expansion, command substitution, + and arithmetic expansion. Dequote the resulting WORD_LIST before + returning it, but do not perform word splitting. The call to + remove_quoted_nulls () is in here because word splitting normally + takes care of quote removal. */ +WORD_LIST * +expand_string_unsplit (string, quoted) + char *string; + int quoted; +{ + WORD_LIST *value; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + expand_no_split_dollar_star = 1; + value = expand_string_internal (string, quoted); + expand_no_split_dollar_star = 0; + + if (value) + { + if (value->word) + { + remove_quoted_nulls (value->word->word); + value->word->flags &= ~W_HASQUOTEDNULL; + } + dequote_list (value); + } + return (value); +} + +/* Expand the rhs of an assignment statement */ +WORD_LIST * +expand_string_assignment (string, quoted) + char *string; + int quoted; +{ + WORD_DESC td; + WORD_LIST *value; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + expand_no_split_dollar_star = 1; + + td.flags = W_ASSIGNRHS; + td.word = savestring (string); + value = call_expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL); + FREE (td.word); + + expand_no_split_dollar_star = 0; + + if (value) + { + if (value->word) + { + remove_quoted_nulls (value->word->word); + value->word->flags &= ~W_HASQUOTEDNULL; + } + dequote_list (value); + } + return (value); +} + + +/* Expand one of the PS? prompt strings. This is a sort of combination of + expand_string_unsplit and expand_string_internal, but returns the + passed string when an error occurs. Might want to trap other calls + to jump_to_top_level here so we don't endlessly loop. */ +WORD_LIST * +expand_prompt_string (string, quoted, wflags) + char *string; + int quoted; + int wflags; +{ + WORD_LIST *value; + WORD_DESC td; + + if (string == 0 || *string == 0) + return ((WORD_LIST *)NULL); + + td.flags = wflags; + td.word = savestring (string); + + no_longjmp_on_fatal_error = 1; + value = expand_word_internal (&td, quoted, 0, (int *)NULL, (int *)NULL); + no_longjmp_on_fatal_error = 0; + + if (value == &expand_word_error || value == &expand_word_fatal) + { + value = make_word_list (make_bare_word (string), (WORD_LIST *)NULL); + return value; + } + FREE (td.word); + if (value) + { + if (value->word) + { + remove_quoted_nulls (value->word->word); + value->word->flags &= ~W_HASQUOTEDNULL; + } + dequote_list (value); + } + return (value); +} + +/* Expand STRING just as if you were expanding a word, but do not dequote + the resultant WORD_LIST. This is called only from within this file, + and is used to correctly preserve quoted characters when expanding + things like ${1+"$@"}. This does parameter expansion, command + substitution, arithmetic expansion, and word splitting. */ +static WORD_LIST * +expand_string_leave_quoted (string, quoted) + char *string; + int quoted; +{ + WORD_LIST *tlist; + WORD_LIST *tresult; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + tlist = expand_string_internal (string, quoted); + + if (tlist) + { + tresult = word_list_split (tlist); + dispose_words (tlist); + return (tresult); + } + return ((WORD_LIST *)NULL); +} + +/* This does not perform word splitting or dequote the WORD_LIST + it returns. */ +static WORD_LIST * +expand_string_for_rhs (string, quoted, dollar_at_p, has_dollar_at) + char *string; + int quoted, *dollar_at_p, *has_dollar_at; +{ + WORD_DESC td; + WORD_LIST *tresult; + + if (string == 0 || *string == '\0') + return (WORD_LIST *)NULL; + + td.flags = 0; + td.word = string; + tresult = call_expand_word_internal (&td, quoted, 1, dollar_at_p, has_dollar_at); + return (tresult); +} + +/* Expand STRING just as if you were expanding a word. This also returns + a list of words. Note that filename globbing is *NOT* done for word + or string expansion, just when the shell is expanding a command. This + does parameter expansion, command substitution, arithmetic expansion, + and word splitting. Dequote the resultant WORD_LIST before returning. */ +WORD_LIST * +expand_string (string, quoted) + char *string; + int quoted; +{ + WORD_LIST *result; + + if (string == 0 || *string == '\0') + return ((WORD_LIST *)NULL); + + result = expand_string_leave_quoted (string, quoted); + return (result ? dequote_list (result) : result); +} + +/*************************************************** + * * + * Functions to handle quoting chars * + * * + ***************************************************/ + +/* Conventions: + + A string with s[0] == CTLNUL && s[1] == 0 is a quoted null string. + The parser passes CTLNUL as CTLESC CTLNUL. */ + +/* Quote escape characters in string s, but no other characters. This is + used to protect CTLESC and CTLNUL in variable values from the rest of + the word expansion process after the variable is expanded (word splitting + and filename generation). If IFS is null, we quote spaces as well, just + in case we split on spaces later (in the case of unquoted $@, we will + eventually attempt to split the entire word on spaces). Corresponding + code exists in dequote_escapes. Even if we don't end up splitting on + spaces, quoting spaces is not a problem. This should never be called on + a string that is quoted with single or double quotes or part of a here + document (effectively double-quoted). */ +char * +quote_escapes (string) + char *string; +{ + register char *s, *t; + size_t slen; + char *result, *send; + int quote_spaces, skip_ctlesc, skip_ctlnul; + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + + quote_spaces = (ifs_value && *ifs_value == 0); + + for (skip_ctlesc = skip_ctlnul = 0, s = ifs_value; s && *s; s++) + skip_ctlesc |= *s == CTLESC, skip_ctlnul |= *s == CTLNUL; + + t = result = (char *)xmalloc ((slen * 2) + 1); + s = string; + + while (*s) + { + if ((skip_ctlesc == 0 && *s == CTLESC) || (skip_ctlnul == 0 && *s == CTLNUL) || (quote_spaces && *s == ' ')) + *t++ = CTLESC; + COPY_CHAR_P (t, s, send); + } + *t = '\0'; + return (result); +} + +static WORD_LIST * +list_quote_escapes (list) + WORD_LIST *list; +{ + register WORD_LIST *w; + char *t; + + for (w = list; w; w = w->next) + { + t = w->word->word; + w->word->word = quote_escapes (t); + free (t); + } + return list; +} + +/* Inverse of quote_escapes; remove CTLESC protecting CTLESC or CTLNUL. + + The parser passes us CTLESC as CTLESC CTLESC and CTLNUL as CTLESC CTLNUL. + This is necessary to make unquoted CTLESC and CTLNUL characters in the + data stream pass through properly. + + We need to remove doubled CTLESC characters inside quoted strings before + quoting the entire string, so we do not double the number of CTLESC + characters. + + Also used by parts of the pattern substitution code. */ +char * +dequote_escapes (string) + char *string; +{ + register char *s, *t, *s1; + size_t slen; + char *result, *send; + int quote_spaces; + DECLARE_MBSTATE; + + if (string == 0) + return string; + + slen = strlen (string); + send = string + slen; + + t = result = (char *)xmalloc (slen + 1); + + if (strchr (string, CTLESC) == 0) + return (strcpy (result, string)); + + quote_spaces = (ifs_value && *ifs_value == 0); + + s = string; + while (*s) + { + if (*s == CTLESC && (s[1] == CTLESC || s[1] == CTLNUL || (quote_spaces && s[1] == ' '))) + { + s++; + if (*s == '\0') + break; + } + COPY_CHAR_P (t, s, send); + } + *t = '\0'; + return result; +} + +/* Return a new string with the quoted representation of character C. + This turns "" into QUOTED_NULL, so the W_HASQUOTEDNULL flag needs to be + set in any resultant WORD_DESC where this value is the word. */ +static char * +make_quoted_char (c) + int c; +{ + char *temp; + + temp = (char *)xmalloc (3); + if (c == 0) + { + temp[0] = CTLNUL; + temp[1] = '\0'; + } + else + { + temp[0] = CTLESC; + temp[1] = c; + temp[2] = '\0'; + } + return (temp); +} + +/* Quote STRING, returning a new string. This turns "" into QUOTED_NULL, so + the W_HASQUOTEDNULL flag needs to be set in any resultant WORD_DESC where + this value is the word. */ +char * +quote_string (string) + char *string; +{ + register char *t; + size_t slen; + char *result, *send; + + if (*string == 0) + { + result = (char *)xmalloc (2); + result[0] = CTLNUL; + result[1] = '\0'; + } + else + { + DECLARE_MBSTATE; + + slen = strlen (string); + send = string + slen; + + result = (char *)xmalloc ((slen * 2) + 1); + + for (t = result; string < send; ) + { + *t++ = CTLESC; + COPY_CHAR_P (t, string, send); + } + *t = '\0'; + } + return (result); +} + +/* De-quote quoted characters in STRING. */ +char * +dequote_string (string) + char *string; +{ + register char *s, *t; + size_t slen; + char *result, *send; + DECLARE_MBSTATE; + + slen = strlen (string); + + t = result = (char *)xmalloc (slen + 1); + + if (QUOTED_NULL (string)) + { + result[0] = '\0'; + return (result); + } + + /* If no character in the string can be quoted, don't bother examining + each character. Just return a copy of the string passed to us. */ + if (strchr (string, CTLESC) == NULL) + return (strcpy (result, string)); + + send = string + slen; + s = string; + while (*s) + { + if (*s == CTLESC) + { + s++; + if (*s == '\0') + break; + } + COPY_CHAR_P (t, s, send); + } + + *t = '\0'; + return (result); +} + +/* Quote the entire WORD_LIST list. */ +static WORD_LIST * +quote_list (list) + WORD_LIST *list; +{ + register WORD_LIST *w; + char *t; + + for (w = list; w; w = w->next) + { + t = w->word->word; + w->word->word = quote_string (t); + if (*t == 0) + w->word->flags |= W_HASQUOTEDNULL; /* XXX - turn on W_HASQUOTEDNULL here? */ + w->word->flags |= W_QUOTED; + free (t); + } + return list; +} + +/* De-quote quoted characters in each word in LIST. */ +WORD_LIST * +dequote_list (list) + WORD_LIST *list; +{ + register char *s; + register WORD_LIST *tlist; + + for (tlist = list; tlist; tlist = tlist->next) + { + s = dequote_string (tlist->word->word); + if (QUOTED_NULL (tlist->word->word)) + tlist->word->flags &= ~W_HASQUOTEDNULL; + free (tlist->word->word); + tlist->word->word = s; + } + return list; +} + +/* Remove CTLESC protecting a CTLESC or CTLNUL in place. Return the passed + string. */ +char * +remove_quoted_escapes (string) + char *string; +{ + char *t; + + if (string) + { + t = dequote_escapes (string); + strcpy (string, t); + free (t); + } + + return (string); +} + +/* Perform quoted null character removal on STRING. We don't allow any + quoted null characters in the middle or at the ends of strings because + of how expand_word_internal works. remove_quoted_nulls () turns + STRING into an empty string iff it only consists of a quoted null, + and removes all unquoted CTLNUL characters. */ +char * +remove_quoted_nulls (string) + char *string; +{ + register size_t slen; + register int i, j, prev_i; + DECLARE_MBSTATE; + + if (strchr (string, CTLNUL) == 0) /* XXX */ + return string; /* XXX */ + + slen = strlen (string); + i = j = 0; + + while (i < slen) + { + if (string[i] == CTLESC) + { + /* Old code had j++, but we cannot assume that i == j at this + point -- what if a CTLNUL has already been removed from the + string? We don't want to drop the CTLESC or recopy characters + that we've already copied down. */ + i++; string[j++] = CTLESC; + if (i == slen) + break; + } + else if (string[i] == CTLNUL) + i++; + + prev_i = i; + ADVANCE_CHAR (string, slen, i); + if (j < prev_i) + { + do string[j++] = string[prev_i++]; while (prev_i < i); + } + else + j = i; + } + string[j] = '\0'; + + return (string); +} + +/* Perform quoted null character removal on each element of LIST. + This modifies LIST. */ +void +word_list_remove_quoted_nulls (list) + WORD_LIST *list; +{ + register WORD_LIST *t; + + for (t = list; t; t = t->next) + { + remove_quoted_nulls (t->word->word); + t->word->flags &= ~W_HASQUOTEDNULL; + } +} + +/* **************************************************************** */ +/* */ +/* Functions for Matching and Removing Patterns */ +/* */ +/* **************************************************************** */ + +#if defined (HANDLE_MULTIBYTE) +#if 0 /* Currently unused */ +static unsigned char * +mb_getcharlens (string, len) + char *string; + int len; +{ + int i, offset, last; + unsigned char *ret; + char *p; + DECLARE_MBSTATE; + + i = offset = 0; + last = 0; + ret = (unsigned char *)xmalloc (len); + memset (ret, 0, len); + while (string[last]) + { + ADVANCE_CHAR (string, len, offset); + ret[last] = offset - last; + last = offset; + } + return ret; +} +#endif +#endif + +/* Remove the portion of PARAM matched by PATTERN according to OP, where OP + can have one of 4 values: + RP_LONG_LEFT remove longest matching portion at start of PARAM + RP_SHORT_LEFT remove shortest matching portion at start of PARAM + RP_LONG_RIGHT remove longest matching portion at end of PARAM + RP_SHORT_RIGHT remove shortest matching portion at end of PARAM +*/ + +#define RP_LONG_LEFT 1 +#define RP_SHORT_LEFT 2 +#define RP_LONG_RIGHT 3 +#define RP_SHORT_RIGHT 4 + +/* Returns its first argument if nothing matched; new memory otherwise */ +static char * +remove_upattern (param, pattern, op) + char *param, *pattern; + int op; +{ + register int len; + register char *end; + register char *p, *ret, c; + + len = STRLEN (param); + end = param + len; + + switch (op) + { + case RP_LONG_LEFT: /* remove longest match at start */ + for (p = end; p >= param; p--) + { + c = *p; *p = '\0'; + if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + *p = c; + return (savestring (p)); + } + *p = c; + + } + break; + + case RP_SHORT_LEFT: /* remove shortest match at start */ + for (p = param; p <= end; p++) + { + c = *p; *p = '\0'; + if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + *p = c; + return (savestring (p)); + } + *p = c; + } + break; + + case RP_LONG_RIGHT: /* remove longest match at end */ + for (p = param; p <= end; p++) + { + if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + c = *p; *p = '\0'; + ret = savestring (param); + *p = c; + return (ret); + } + } + break; + + case RP_SHORT_RIGHT: /* remove shortest match at end */ + for (p = end; p >= param; p--) + { + if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + c = *p; *p = '\0'; + ret = savestring (param); + *p = c; + return (ret); + } + } + break; + } + + return (param); /* no match, return original string */ +} + +#if defined (HANDLE_MULTIBYTE) +/* Returns its first argument if nothing matched; new memory otherwise */ +static wchar_t * +remove_wpattern (wparam, wstrlen, wpattern, op) + wchar_t *wparam; + size_t wstrlen; + wchar_t *wpattern; + int op; +{ + wchar_t wc, *ret; + int n; + + switch (op) + { + case RP_LONG_LEFT: /* remove longest match at start */ + for (n = wstrlen; n >= 0; n--) + { + wc = wparam[n]; wparam[n] = L'\0'; + if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wparam[n] = wc; + return (wcsdup (wparam + n)); + } + wparam[n] = wc; + } + break; + + case RP_SHORT_LEFT: /* remove shortest match at start */ + for (n = 0; n <= wstrlen; n++) + { + wc = wparam[n]; wparam[n] = L'\0'; + if (wcsmatch (wpattern, wparam, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wparam[n] = wc; + return (wcsdup (wparam + n)); + } + wparam[n] = wc; + } + break; + + case RP_LONG_RIGHT: /* remove longest match at end */ + for (n = 0; n <= wstrlen; n++) + { + if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wc = wparam[n]; wparam[n] = L'\0'; + ret = wcsdup (wparam); + wparam[n] = wc; + return (ret); + } + } + break; + + case RP_SHORT_RIGHT: /* remove shortest match at end */ + for (n = wstrlen; n >= 0; n--) + { + if (wcsmatch (wpattern, wparam + n, FNMATCH_EXTFLAG) != FNM_NOMATCH) + { + wc = wparam[n]; wparam[n] = L'\0'; + ret = wcsdup (wparam); + wparam[n] = wc; + return (ret); + } + } + break; + } + + return (wparam); /* no match, return original string */ +} +#endif /* HANDLE_MULTIBYTE */ + +static char * +remove_pattern (param, pattern, op) + char *param, *pattern; + int op; +{ + char *xret; + + if (param == NULL) + return (param); + if (*param == '\0' || pattern == NULL || *pattern == '\0') /* minor optimization */ + return (savestring (param)); + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + { + wchar_t *ret, *oret; + size_t n; + wchar_t *wparam, *wpattern; + mbstate_t ps; + + n = xdupmbstowcs (&wpattern, NULL, pattern); + if (n == (size_t)-1) + { + xret = remove_upattern (param, pattern, op); + return ((xret == param) ? savestring (param) : xret); + } + n = xdupmbstowcs (&wparam, NULL, param); + if (n == (size_t)-1) + { + free (wpattern); + xret = remove_upattern (param, pattern, op); + return ((xret == param) ? savestring (param) : xret); + } + oret = ret = remove_wpattern (wparam, n, wpattern, op); + /* Don't bother to convert wparam back to multibyte string if nothing + matched; just return copy of original string */ + if (ret == wparam) + { + free (wparam); + free (wpattern); + return (savestring (param)); + } + + free (wparam); + free (wpattern); + + n = strlen (param); + xret = (char *)xmalloc (n + 1); + memset (&ps, '\0', sizeof (mbstate_t)); + n = wcsrtombs (xret, (const wchar_t **)&ret, n, &ps); + xret[n] = '\0'; /* just to make sure */ + free (oret); + return xret; + } + else +#endif + { + xret = remove_upattern (param, pattern, op); + return ((xret == param) ? savestring (param) : xret); + } +} + +static char const *const ccname[] = + { + "ascii:]", "alnum:]", "alpha:]", "blank:]", "cntrl:]", "digit:]", "graph:]", + "lower:]", "print:]", "punct:]", "space:]", "upper:]", "word:]", "xdigit:]" + }; +#define CCNAMESIZ sizeof (ccname) / sizeof (ccname[0]) + +/* Return 1 of the first character of STRING could match the first + character of pattern PAT. Used to avoid n2 calls to strmatch(). */ +static int +match_pattern_char (pat, string) + char *pat, *string; +{ + char c; + + if (*string == 0) + return (0); + + switch (c = *pat++) + { + default: + return (*string == c); + case '\\': + return (*string == *pat); + case '?': + return (*pat == LPAREN ? 1 : (*string != '\0')); + case '*': + return (1); + case '+': + case '!': + case '@': + return (*pat == LPAREN ? 1 : (*string == c)); + case '[': + return (*string != '\0'); + } +} + +/* Match PAT anywhere in STRING and return the match boundaries. + This returns 1 in case of a successful match, 0 otherwise. SP + and EP are pointers into the string where the match begins and + ends, respectively. MTYPE controls what kind of match is attempted. + MATCH_BEG and MATCH_END anchor the match at the beginning and end + of the string, respectively. The longest match is returned. */ +static int +match_upattern (string, pat, mtype, sp, ep) + char *string, *pat; + int mtype; + char **sp, **ep; +{ + int c, len; + register char *p, *p1, *npat; + char *end; + + /* If the pattern doesn't match anywhere in the string, go ahead and + short-circuit right away. A minor optimization, saves a bunch of + unnecessary calls to strmatch (up to N calls for a string of N + characters) if the match is unsuccessful. To preserve the semantics + of the substring matches below, we make sure that the pattern has + `*' as first and last character, making a new pattern if necessary. */ + /* XXX - check this later if I ever implement `**' with special meaning, + since this will potentially result in `**' at the beginning or end */ + len = STRLEN (pat); + if (pat[0] != '*' || (pat[0] == '*' && pat[1] == LPAREN && extended_glob) || pat[len - 1] != '*') + { + p = npat = (char *)xmalloc (len + 3); + p1 = pat; + if (*p1 != '*' || (*p1 == '*' && p1[1] == LPAREN && extended_glob)) + *p++ = '*'; + while (*p1) + *p++ = *p1++; + if (p1[-1] != '*' || p[-2] == '\\') + *p++ = '*'; + *p = '\0'; + } + else + npat = pat; + c = strmatch (npat, string, FNMATCH_EXTFLAG); + if (npat != pat) + free (npat); + if (c == FNM_NOMATCH) + return (0); + + len = STRLEN (string); + end = string + len; + + switch (mtype) + { + case MATCH_ANY: + for (p = string; p <= end; p++) + { + if (match_pattern_char (pat, p)) + { + for (p1 = end; p1 >= p; p1--) + { + c = *p1; *p1 = '\0'; + if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) + { + *p1 = c; + *sp = p; + *ep = p1; + return 1; + } + *p1 = c; + } + } + } + + return (0); + + case MATCH_BEG: + if (match_pattern_char (pat, string) == 0) + return (0); + + for (p = end; p >= string; p--) + { + c = *p; *p = '\0'; + if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0) + { + *p = c; + *sp = string; + *ep = p; + return 1; + } + *p = c; + } + + return (0); + + case MATCH_END: + for (p = string; p <= end; p++) + { + if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) + { + *sp = p; + *ep = end; + return 1; + } + } + + return (0); + } + + return (0); +} + +#if defined (HANDLE_MULTIBYTE) +/* Return 1 of the first character of WSTRING could match the first + character of pattern WPAT. Wide character version. */ +static int +match_pattern_wchar (wpat, wstring) + wchar_t *wpat, *wstring; +{ + wchar_t wc; + + if (*wstring == 0) + return (0); + + switch (wc = *wpat++) + { + default: + return (*wstring == wc); + case L'\\': + return (*wstring == *wpat); + case L'?': + return (*wpat == LPAREN ? 1 : (*wstring != L'\0')); + case L'*': + return (1); + case L'+': + case L'!': + case L'@': + return (*wpat == LPAREN ? 1 : (*wstring == wc)); + case L'[': + return (*wstring != L'\0'); + } +} + +static wchar_t *wccname[] = + { + L"ascii:]", L"alnum:]", L"alpha:]", L"blank:]", L"cntrl:]", + L"digit:]", L"graph:]", L"lower:]", L"print:]", L"punct:]", + L"space:]", L"upper:]", L"word:]", L"xdigit:]" + }; +#define WCCNAMESIZ sizeof (wccname) / sizeof (wccname[0]) + +static int +wccmatch (wpat) + wchar_t *wpat; +{ + wchar_t *wc; + wchar_t *name; + size_t wlen; + int i, c1, c2; + + for (i = 0; i < WCCNAMESIZ; i++) + { + name = wccname[i]; + wlen = wcslen (name); + if (wcsncmp (wpat, name, wlen) == 0) + return wlen; + } + return -1; +} + +static int +wmatchlen (wpat, wmax) + wchar_t *wpat; + size_t wmax; +{ + wchar_t wc, *wbrack; + int matlen, t, in_cclass, in_collsym, in_equiv; + + if (*wpat == 0) + return (0); + + matlen = 0; + while (wc = *wpat++) + { + switch (wc) + { + default: + matlen++; + break; + case L'\\': + if (*wpat == 0) + return ++matlen; + else + matlen++; + break; + case L'?': + if (*wpat == LPAREN) + return (matlen = wmax); /* XXX for now */ + else + matlen++; + break; + case L'*': + return (matlen = wmax); + case L'+': + case L'!': + case L'@': + if (*wpat == LPAREN) + return (matlen = wmax); /* XXX for now */ + else + matlen++; + break; + case L'[': + /* scan for ending `]', skipping over embedded [:...:] */ + wbrack = wpat; + wc = *wpat++; + do + { + if (wc == 0) + { + matlen += wpat - wbrack - 1; /* incremented below */ + break; + } + else if (wc == L'\\') + { + wc = *wpat++; + if (*wpat == 0) + break; + } + else if (wc == L'[' && *wpat == L':') /* character class */ + { + wpat++; + if (*wpat == L']') /* right bracket can appear as first char in pattern */ + wpat++; + in_cclass = 1; + } + else if (in_cclass && wc == L':' && *wpat == L']') + { + wpat++; + in_cclass = 0; + } + else if (wc == L'[' && *wpat == L'.') /* collating symbol */ + { + wpat++; + if (*wpat == L']') /* right bracket can appear as first char in pattern */ + wpat++; + in_collsym = 1; + } + else if (in_collsym && wc == L'.' && *wpat == L']') + { + wpat++; + in_collsym = 0; + } + else if (wc == L'[' && *wpat == L'=') /* equivalence class */ + { + wpat++; + if (*wpat == L']') /* right bracket can appear as first char in pattern */ + wpat++; + in_equiv = 1; + } + else if (in_equiv && wc == L':' && *wpat == L']') + { + wpat++; + in_equiv = 0; + } + } + while ((wc = *wpat++) != L']'); + matlen++; /* bracket expression can only match one char */ + break; + } + } + + return matlen; +} + +/* Match WPAT anywhere in WSTRING and return the match boundaries. + This returns 1 in case of a successful match, 0 otherwise. Wide + character version. */ +static int +match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) + wchar_t *wstring; + char **indices; + size_t wstrlen; + wchar_t *wpat; + int mtype; + char **sp, **ep; +{ + wchar_t wc, *wp, *nwpat, *wp1; + int len, mlen; +#if 0 + size_t n, n1; /* Apple's gcc seems to miscompile this badly */ +#else + int n, n1; +#endif + + /* If the pattern doesn't match anywhere in the string, go ahead and + short-circuit right away. A minor optimization, saves a bunch of + unnecessary calls to strmatch (up to N calls for a string of N + characters) if the match is unsuccessful. To preserve the semantics + of the substring matches below, we make sure that the pattern has + `*' as first and last character, making a new pattern if necessary. */ + /* XXX - check this later if I ever implement `**' with special meaning, + since this will potentially result in `**' at the beginning or end */ + len = wcslen (wpat); + if (wpat[0] != L'*' || (wpat[0] == L'*' && wpat[1] == WLPAREN && extended_glob) || wpat[len - 1] != L'*') + { + wp = nwpat = (wchar_t *)xmalloc ((len + 3) * sizeof (wchar_t)); + wp1 = wpat; + if (*wp1 != L'*' || (*wp1 == '*' && wp1[1] == WLPAREN && extended_glob)) + *wp++ = L'*'; + while (*wp1 != L'\0') + *wp++ = *wp1++; + if (wp1[-1] != L'*' || wp1[-2] == L'\\') + *wp++ = L'*'; + *wp = '\0'; + } + else + nwpat = wpat; + len = wcsmatch (nwpat, wstring, FNMATCH_EXTFLAG); + if (nwpat != wpat) + free (nwpat); + if (len == FNM_NOMATCH) + return (0); + + mlen = wmatchlen (wpat, wstrlen); +itrace("wmatchlen (%ls) -> %d", wpat, mlen); + switch (mtype) + { + case MATCH_ANY: + for (n = 0; n <= wstrlen; n++) + { + if (match_pattern_wchar (wpat, wstring + n)) + { + for (n1 = wstrlen; n1 >= n; n1--) + { + wc = wstring[n1]; wstring[n1] = L'\0'; + if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) + { + wstring[n1] = wc; + *sp = indices[n]; + *ep = indices[n1]; + return 1; + } + wstring[n1] = wc; + } + } + } + + return (0); + + case MATCH_BEG: + if (match_pattern_wchar (wpat, wstring) == 0) + return (0); + + for (n = wstrlen; n >= 0; n--) + { + wc = wstring[n]; wstring[n] = L'\0'; + if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG) == 0) + { + wstring[n] = wc; + *sp = indices[0]; + *ep = indices[n]; + return 1; + } + wstring[n] = wc; + } + + return (0); + + case MATCH_END: + for (n = 0; n <= wstrlen; n++) + { + if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) + { + *sp = indices[n]; + *ep = indices[wstrlen]; + return 1; + } + } + + return (0); + } + + return (0); +} +#endif /* HANDLE_MULTIBYTE */ + +static int +match_pattern (string, pat, mtype, sp, ep) + char *string, *pat; + int mtype; + char **sp, **ep; +{ +#if defined (HANDLE_MULTIBYTE) + int ret; + size_t n; + wchar_t *wstring, *wpat; + char **indices; +#endif + + if (string == 0 || *string == 0 || pat == 0 || *pat == 0) + return (0); + +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + { + n = xdupmbstowcs (&wpat, NULL, pat); + if (n == (size_t)-1) + return (match_upattern (string, pat, mtype, sp, ep)); + n = xdupmbstowcs (&wstring, &indices, string); + if (n == (size_t)-1) + { + free (wpat); + return (match_upattern (string, pat, mtype, sp, ep)); + } + ret = match_wpattern (wstring, indices, n, wpat, mtype, sp, ep); + + free (wpat); + free (wstring); + free (indices); + + return (ret); + } + else +#endif + return (match_upattern (string, pat, mtype, sp, ep)); +} + +static int +getpatspec (c, value) + int c; + char *value; +{ + if (c == '#') + return ((*value == '#') ? RP_LONG_LEFT : RP_SHORT_LEFT); + else /* c == '%' */ + return ((*value == '%') ? RP_LONG_RIGHT : RP_SHORT_RIGHT); +} + +/* Posix.2 says that the WORD should be run through tilde expansion, + parameter expansion, command substitution and arithmetic expansion. + This leaves the result quoted, so quote_string_for_globbing () has + to be called to fix it up for strmatch (). If QUOTED is non-zero, + it means that the entire expression was enclosed in double quotes. + This means that quoting characters in the pattern do not make any + special pattern characters quoted. For example, the `*' in the + following retains its special meaning: "${foo#'*'}". */ +static char * +getpattern (value, quoted, expandpat) + char *value; + int quoted, expandpat; +{ + char *pat, *tword; + WORD_LIST *l; +#if 0 + int i; +#endif + /* There is a problem here: how to handle single or double quotes in the + pattern string when the whole expression is between double quotes? + POSIX.2 says that enclosing double quotes do not cause the pattern to + be quoted, but does that leave us a problem with @ and array[@] and their + expansions inside a pattern? */ +#if 0 + if (expandpat && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *tword) + { + i = 0; + pat = string_extract_double_quoted (tword, &i, 1); + free (tword); + tword = pat; + } +#endif + + /* expand_string_for_rhs () leaves WORD quoted and does not perform + word splitting. */ + l = *value ? expand_string_for_rhs (value, + (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? Q_PATQUOTE : quoted, + (int *)NULL, (int *)NULL) + : (WORD_LIST *)0; + pat = string_list (l); + dispose_words (l); + if (pat) + { + tword = quote_string_for_globbing (pat, QGLOB_CVTNULL); + free (pat); + pat = tword; + } + return (pat); +} + +#if 0 +/* Handle removing a pattern from a string as a result of ${name%[%]value} + or ${name#[#]value}. */ +static char * +variable_remove_pattern (value, pattern, patspec, quoted) + char *value, *pattern; + int patspec, quoted; +{ + char *tword; + + tword = remove_pattern (value, pattern, patspec); + + return (tword); +} +#endif + +static char * +list_remove_pattern (list, pattern, patspec, itype, quoted) + WORD_LIST *list; + char *pattern; + int patspec, itype, quoted; +{ + WORD_LIST *new, *l; + WORD_DESC *w; + char *tword; + + for (new = (WORD_LIST *)NULL, l = list; l; l = l->next) + { + tword = remove_pattern (l->word->word, pattern, patspec); + w = alloc_word_desc (); + w->word = tword ? tword : savestring (""); + new = make_word_list (w, new); + } + + l = REVERSE_LIST (new, WORD_LIST *); + tword = string_list_pos_params (itype, l, quoted); + dispose_words (l); + + return (tword); +} + +static char * +parameter_list_remove_pattern (itype, pattern, patspec, quoted) + int itype; + char *pattern; + int patspec, quoted; +{ + char *ret; + WORD_LIST *list; + + list = list_rest_of_args (); + if (list == 0) + return ((char *)NULL); + ret = list_remove_pattern (list, pattern, patspec, itype, quoted); + dispose_words (list); + return (ret); +} + +#if defined (ARRAY_VARS) +static char * +array_remove_pattern (var, pattern, patspec, varname, quoted) + SHELL_VAR *var; + char *pattern; + int patspec; + char *varname; /* so we can figure out how it's indexed */ + int quoted; +{ + ARRAY *a; + HASH_TABLE *h; + int itype; + char *ret; + WORD_LIST *list; + SHELL_VAR *v; + + /* compute itype from varname here */ + v = array_variable_part (varname, &ret, 0); + itype = ret[0]; + + a = (v && array_p (v)) ? array_cell (v) : 0; + h = (v && assoc_p (v)) ? assoc_cell (v) : 0; + + list = a ? array_to_word_list (a) : (h ? assoc_to_word_list (h) : 0); + if (list == 0) + return ((char *)NULL); + ret = list_remove_pattern (list, pattern, patspec, itype, quoted); + dispose_words (list); + + return ret; +} +#endif /* ARRAY_VARS */ + +static char * +parameter_brace_remove_pattern (varname, value, ind, patstr, rtype, quoted, flags) + char *varname, *value; + int ind; + char *patstr; + int rtype, quoted, flags; +{ + int vtype, patspec, starsub; + char *temp1, *val, *pattern; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + return ((char *)NULL); + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + patspec = getpatspec (rtype, patstr); + if (patspec == RP_LONG_LEFT || patspec == RP_LONG_RIGHT) + patstr++; + + /* Need to pass getpattern newly-allocated memory in case of expansion -- + the expansion code will free the passed string on an error. */ + temp1 = savestring (patstr); + pattern = getpattern (temp1, quoted, 1); + free (temp1); + + temp1 = (char *)NULL; /* shut up gcc */ + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + temp1 = remove_pattern (val, pattern, patspec); + if (vtype == VT_VARIABLE) + FREE (val); + if (temp1) + { + val = (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + ? quote_string (temp1) + : quote_escapes (temp1); + free (temp1); + temp1 = val; + } + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + temp1 = array_remove_pattern (v, pattern, patspec, varname, quoted); + if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)) + { + val = quote_escapes (temp1); + free (temp1); + temp1 = val; + } + break; +#endif + case VT_POSPARMS: + temp1 = parameter_list_remove_pattern (varname[0], pattern, patspec, quoted); + if (temp1 && ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) == 0)) + { + val = quote_escapes (temp1); + free (temp1); + temp1 = val; + } + break; + } + + FREE (pattern); + return temp1; +} + +/******************************************* + * * + * Functions to expand WORD_DESCs * + * * + *******************************************/ + +/* Expand WORD, performing word splitting on the result. This does + parameter expansion, command substitution, arithmetic expansion, + word splitting, and quote removal. */ + +WORD_LIST * +expand_word (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_LIST *result, *tresult; + + tresult = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL); + result = word_list_split (tresult); + dispose_words (tresult); + return (result ? dequote_list (result) : result); +} + +/* Expand WORD, but do not perform word splitting on the result. This + does parameter expansion, command substitution, arithmetic expansion, + and quote removal. */ +WORD_LIST * +expand_word_unsplit (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_LIST *result; + + expand_no_split_dollar_star = 1; +#if defined (HANDLE_MULTIBYTE) + if (ifs_firstc[0] == 0) +#else + if (ifs_firstc == 0) +#endif + word->flags |= W_NOSPLIT; + result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL); + expand_no_split_dollar_star = 0; + + return (result ? dequote_list (result) : result); +} + +/* Perform shell expansions on WORD, but do not perform word splitting or + quote removal on the result. Virtually identical to expand_word_unsplit; + could be combined if implementations don't diverge. */ +WORD_LIST * +expand_word_leave_quoted (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_LIST *result; + + expand_no_split_dollar_star = 1; +#if defined (HANDLE_MULTIBYTE) + if (ifs_firstc[0] == 0) +#else + if (ifs_firstc == 0) +#endif + word->flags |= W_NOSPLIT; + word->flags |= W_NOSPLIT2; + result = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL); + expand_no_split_dollar_star = 0; + + return result; +} + +#if defined (PROCESS_SUBSTITUTION) + +/*****************************************************************/ +/* */ +/* Hacking Process Substitution */ +/* */ +/*****************************************************************/ + +#if !defined (HAVE_DEV_FD) +/* Named pipes must be removed explicitly with `unlink'. This keeps a list + of FIFOs the shell has open. unlink_fifo_list will walk the list and + unlink all of them. add_fifo_list adds the name of an open FIFO to the + list. NFIFO is a count of the number of FIFOs in the list. */ +#define FIFO_INCR 20 + +struct temp_fifo { + char *file; + pid_t proc; +}; + +static struct temp_fifo *fifo_list = (struct temp_fifo *)NULL; +static int nfifo; +static int fifo_list_size; + +char * +copy_fifo_list (sizep) + int *sizep; +{ + if (sizep) + *sizep = 0; + return (char *)NULL; +} + +static void +add_fifo_list (pathname) + char *pathname; +{ + if (nfifo >= fifo_list_size - 1) + { + fifo_list_size += FIFO_INCR; + fifo_list = (struct temp_fifo *)xrealloc (fifo_list, + fifo_list_size * sizeof (struct temp_fifo)); + } + + fifo_list[nfifo].file = savestring (pathname); + nfifo++; +} + +void +unlink_fifo (i) + int i; +{ + if ((fifo_list[i].proc == -1) || (kill(fifo_list[i].proc, 0) == -1)) + { + unlink (fifo_list[i].file); + free (fifo_list[i].file); + fifo_list[i].file = (char *)NULL; + fifo_list[i].proc = -1; + } +} + +void +unlink_fifo_list () +{ + int saved, i, j; + + if (nfifo == 0) + return; + + for (i = saved = 0; i < nfifo; i++) + { + if ((fifo_list[i].proc == -1) || (kill(fifo_list[i].proc, 0) == -1)) + { + unlink (fifo_list[i].file); + free (fifo_list[i].file); + fifo_list[i].file = (char *)NULL; + fifo_list[i].proc = -1; + } + else + saved++; + } + + /* If we didn't remove some of the FIFOs, compact the list. */ + if (saved) + { + for (i = j = 0; i < nfifo; i++) + if (fifo_list[i].file) + { + fifo_list[j].file = fifo_list[i].file; + fifo_list[j].proc = fifo_list[i].proc; + j++; + } + nfifo = j; + } + else + nfifo = 0; +} + +/* Take LIST, which is a bitmap denoting active FIFOs in fifo_list + from some point in the past, and close all open FIFOs in fifo_list + that are not marked as active in LIST. If LIST is NULL, close + everything in fifo_list. LSIZE is the number of elements in LIST, in + case it's larger than fifo_list_size (size of fifo_list). */ +void +close_new_fifos (list, lsize) + char *list; + int lsize; +{ + int i; + + if (list == 0) + { + unlink_fifo_list (); + return; + } + + for (i = 0; i < lsize; i++) + if (list[i] == 0 && i < fifo_list_size && fifo_list[i].proc != -1) + unlink_fifo (i); + + for (i = lsize; i < fifo_list_size; i++) + unlink_fifo (i); +} + +int +fifos_pending () +{ + return nfifo; +} + +int +num_fifos () +{ + return nfifo; +} + +static char * +make_named_pipe () +{ + char *tname; + + tname = sh_mktmpname ("sh-np", MT_USERANDOM|MT_USETMPDIR); + if (mkfifo (tname, 0600) < 0) + { + free (tname); + return ((char *)NULL); + } + + add_fifo_list (tname); + return (tname); +} + +#else /* HAVE_DEV_FD */ + +/* DEV_FD_LIST is a bitmap of file descriptors attached to pipes the shell + has open to children. NFDS is a count of the number of bits currently + set in DEV_FD_LIST. TOTFDS is a count of the highest possible number + of open files. */ +static char *dev_fd_list = (char *)NULL; +static int nfds; +static int totfds; /* The highest possible number of open files. */ + +char * +copy_fifo_list (sizep) + int *sizep; +{ + char *ret; + + if (nfds == 0 || totfds == 0) + { + if (sizep) + *sizep = 0; + return (char *)NULL; + } + + if (sizep) + *sizep = totfds; + ret = (char *)xmalloc (totfds); + return (memcpy (ret, dev_fd_list, totfds)); +} + +static void +add_fifo_list (fd) + int fd; +{ + if (dev_fd_list == 0 || fd >= totfds) + { + int ofds; + + ofds = totfds; + totfds = getdtablesize (); + if (totfds < 0 || totfds > 256) + totfds = 256; + if (fd >= totfds) + totfds = fd + 2; + + dev_fd_list = (char *)xrealloc (dev_fd_list, totfds); + memset (dev_fd_list + ofds, '\0', totfds - ofds); + } + + dev_fd_list[fd] = 1; + nfds++; +} + +int +fifos_pending () +{ + return 0; /* used for cleanup; not needed with /dev/fd */ +} + +int +num_fifos () +{ + return nfds; +} + +void +unlink_fifo (fd) + int fd; +{ + if (dev_fd_list[fd]) + { + close (fd); + dev_fd_list[fd] = 0; + nfds--; + } +} + +void +unlink_fifo_list () +{ + register int i; + + if (nfds == 0) + return; + + for (i = 0; nfds && i < totfds; i++) + unlink_fifo (i); + + nfds = 0; +} + +/* Take LIST, which is a snapshot copy of dev_fd_list from some point in + the past, and close all open fds in dev_fd_list that are not marked + as open in LIST. If LIST is NULL, close everything in dev_fd_list. + LSIZE is the number of elements in LIST, in case it's larger than + totfds (size of dev_fd_list). */ +void +close_new_fifos (list, lsize) + char *list; + int lsize; +{ + int i; + + if (list == 0) + { + unlink_fifo_list (); + return; + } + + for (i = 0; i < lsize; i++) + if (list[i] == 0 && i < totfds && dev_fd_list[i]) + unlink_fifo (i); + + for (i = lsize; i < totfds; i++) + unlink_fifo (i); +} + +#if defined (NOTDEF) +print_dev_fd_list () +{ + register int i; + + fprintf (stderr, "pid %ld: dev_fd_list:", (long)getpid ()); + fflush (stderr); + + for (i = 0; i < totfds; i++) + { + if (dev_fd_list[i]) + fprintf (stderr, " %d", i); + } + fprintf (stderr, "\n"); +} +#endif /* NOTDEF */ + +static char * +make_dev_fd_filename (fd) + int fd; +{ + char *ret, intbuf[INT_STRLEN_BOUND (int) + 1], *p; + + ret = (char *)xmalloc (sizeof (DEV_FD_PREFIX) + 8); + + strcpy (ret, DEV_FD_PREFIX); + p = inttostr (fd, intbuf, sizeof (intbuf)); + strcpy (ret + sizeof (DEV_FD_PREFIX) - 1, p); + + add_fifo_list (fd); + return (ret); +} + +#endif /* HAVE_DEV_FD */ + +/* Return a filename that will open a connection to the process defined by + executing STRING. HAVE_DEV_FD, if defined, means open a pipe and return + a filename in /dev/fd corresponding to a descriptor that is one of the + ends of the pipe. If not defined, we use named pipes on systems that have + them. Systems without /dev/fd and named pipes are out of luck. + + OPEN_FOR_READ_IN_CHILD, if 1, means open the named pipe for reading or + use the read end of the pipe and dup that file descriptor to fd 0 in + the child. If OPEN_FOR_READ_IN_CHILD is 0, we open the named pipe for + writing or use the write end of the pipe in the child, and dup that + file descriptor to fd 1 in the child. The parent does the opposite. */ + +static char * +process_substitute (string, open_for_read_in_child) + char *string; + int open_for_read_in_child; +{ + char *pathname; + int fd, result; + pid_t old_pid, pid; +#if defined (HAVE_DEV_FD) + int parent_pipe_fd, child_pipe_fd; + int fildes[2]; +#endif /* HAVE_DEV_FD */ +#if defined (JOB_CONTROL) + pid_t old_pipeline_pgrp; +#endif + + if (!string || !*string || wordexp_only) + return ((char *)NULL); + +#if !defined (HAVE_DEV_FD) + pathname = make_named_pipe (); +#else /* HAVE_DEV_FD */ + if (pipe (fildes) < 0) + { + sys_error (_("cannot make pipe for process substitution")); + return ((char *)NULL); + } + /* If OPEN_FOR_READ_IN_CHILD == 1, we want to use the write end of + the pipe in the parent, otherwise the read end. */ + parent_pipe_fd = fildes[open_for_read_in_child]; + child_pipe_fd = fildes[1 - open_for_read_in_child]; + /* Move the parent end of the pipe to some high file descriptor, to + avoid clashes with FDs used by the script. */ + parent_pipe_fd = move_to_high_fd (parent_pipe_fd, 1, 64); + + pathname = make_dev_fd_filename (parent_pipe_fd); +#endif /* HAVE_DEV_FD */ + + if (pathname == 0) + { + sys_error (_("cannot make pipe for process substitution")); + return ((char *)NULL); + } + + old_pid = last_made_pid; + +#if defined (JOB_CONTROL) + old_pipeline_pgrp = pipeline_pgrp; + pipeline_pgrp = shell_pgrp; + save_pipeline (1); +#endif /* JOB_CONTROL */ + + pid = make_child ((char *)NULL, 1); + if (pid == 0) + { + reset_terminating_signals (); /* XXX */ + free_pushed_string_input (); + /* Cancel traps, in trap.c. */ + restore_original_signals (); /* XXX - what about special builtins? bash-4.2 */ + setup_async_signals (); + subshell_environment |= SUBSHELL_COMSUB|SUBSHELL_PROCSUB; + } + +#if defined (JOB_CONTROL) + set_sigchld_handler (); + stop_making_children (); + /* XXX - should we only do this in the parent? (as in command subst) */ + pipeline_pgrp = old_pipeline_pgrp; +#endif /* JOB_CONTROL */ + + if (pid < 0) + { + sys_error (_("cannot make child for process substitution")); + free (pathname); +#if defined (HAVE_DEV_FD) + close (parent_pipe_fd); + close (child_pipe_fd); +#endif /* HAVE_DEV_FD */ + return ((char *)NULL); + } + + if (pid > 0) + { +#if defined (JOB_CONTROL) + restore_pipeline (1); +#endif + +#if !defined (HAVE_DEV_FD) + fifo_list[nfifo-1].proc = pid; +#endif + + last_made_pid = old_pid; + +#if defined (JOB_CONTROL) && defined (PGRP_PIPE) + close_pgrp_pipe (); +#endif /* JOB_CONTROL && PGRP_PIPE */ + +#if defined (HAVE_DEV_FD) + close (child_pipe_fd); +#endif /* HAVE_DEV_FD */ + + return (pathname); + } + + set_sigint_handler (); + +#if defined (JOB_CONTROL) + set_job_control (0); +#endif /* JOB_CONTROL */ + +#if !defined (HAVE_DEV_FD) + /* Open the named pipe in the child. */ + fd = open (pathname, open_for_read_in_child ? O_RDONLY|O_NONBLOCK : O_WRONLY); + if (fd < 0) + { + /* Two separate strings for ease of translation. */ + if (open_for_read_in_child) + sys_error (_("cannot open named pipe %s for reading"), pathname); + else + sys_error (_("cannot open named pipe %s for writing"), pathname); + + exit (127); + } + if (open_for_read_in_child) + { + if (sh_unset_nodelay_mode (fd) < 0) + { + sys_error (_("cannot reset nodelay mode for fd %d"), fd); + exit (127); + } + } +#else /* HAVE_DEV_FD */ + fd = child_pipe_fd; +#endif /* HAVE_DEV_FD */ + + if (dup2 (fd, open_for_read_in_child ? 0 : 1) < 0) + { + sys_error (_("cannot duplicate named pipe %s as fd %d"), pathname, + open_for_read_in_child ? 0 : 1); + exit (127); + } + + if (fd != (open_for_read_in_child ? 0 : 1)) + close (fd); + + /* Need to close any files that this process has open to pipes inherited + from its parent. */ + if (current_fds_to_close) + { + close_fd_bitmap (current_fds_to_close); + current_fds_to_close = (struct fd_bitmap *)NULL; + } + +#if defined (HAVE_DEV_FD) + /* Make sure we close the parent's end of the pipe and clear the slot + in the fd list so it is not closed later, if reallocated by, for + instance, pipe(2). */ + close (parent_pipe_fd); + dev_fd_list[parent_pipe_fd] = 0; +#endif /* HAVE_DEV_FD */ + + result = parse_and_execute (string, "process substitution", (SEVAL_NONINT|SEVAL_NOHIST)); + +#if !defined (HAVE_DEV_FD) + /* Make sure we close the named pipe in the child before we exit. */ + close (open_for_read_in_child ? 0 : 1); +#endif /* !HAVE_DEV_FD */ + + exit (result); + /*NOTREACHED*/ +} +#endif /* PROCESS_SUBSTITUTION */ + +/***********************************/ +/* */ +/* Command Substitution */ +/* */ +/***********************************/ + +static char * +read_comsub (fd, quoted, rflag) + int fd, quoted; + int *rflag; +{ + char *istring, buf[128], *bufp, *s; + int istring_index, istring_size, c, tflag, skip_ctlesc, skip_ctlnul; + ssize_t bufn; + + istring = (char *)NULL; + istring_index = istring_size = bufn = tflag = 0; + + for (skip_ctlesc = skip_ctlnul = 0, s = ifs_value; s && *s; s++) + skip_ctlesc |= *s == CTLESC, skip_ctlnul |= *s == CTLNUL; + +#ifdef __CYGWIN__ + setmode (fd, O_TEXT); /* we don't want CR/LF, we want Unix-style */ +#endif + + /* Read the output of the command through the pipe. This may need to be + changed to understand multibyte characters in the future. */ + while (1) + { + if (fd < 0) + break; + if (--bufn <= 0) + { + bufn = zread (fd, buf, sizeof (buf)); + if (bufn <= 0) + break; + bufp = buf; + } + c = *bufp++; + + if (c == 0) + { +#if 0 + internal_warning ("read_comsub: ignored null byte in input"); +#endif + continue; + } + + /* Add the character to ISTRING, possibly after resizing it. */ + RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, DEFAULT_ARRAY_SIZE); + + /* This is essentially quote_string inline */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) /* || c == CTLESC || c == CTLNUL */) + istring[istring_index++] = CTLESC; + /* Escape CTLESC and CTLNUL in the output to protect those characters + from the rest of the word expansions (word splitting and globbing.) + This is essentially quote_escapes inline. */ + else if (skip_ctlesc == 0 && c == CTLESC) + { + tflag |= W_HASCTLESC; + istring[istring_index++] = CTLESC; + } + else if ((skip_ctlnul == 0 && c == CTLNUL) || (c == ' ' && (ifs_value && *ifs_value == 0))) + istring[istring_index++] = CTLESC; + + istring[istring_index++] = c; + +#if 0 +#if defined (__CYGWIN__) + if (c == '\n' && istring_index > 1 && istring[istring_index - 2] == '\r') + { + istring_index--; + istring[istring_index - 1] = '\n'; + } +#endif +#endif + } + + if (istring) + istring[istring_index] = '\0'; + + /* If we read no output, just return now and save ourselves some + trouble. */ + if (istring_index == 0) + { + FREE (istring); + if (rflag) + *rflag = tflag; + return (char *)NULL; + } + + /* Strip trailing newlines from the output of the command. */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + { + while (istring_index > 0) + { + if (istring[istring_index - 1] == '\n') + { + --istring_index; + + /* If the newline was quoted, remove the quoting char. */ + if (istring[istring_index - 1] == CTLESC) + --istring_index; + } + else + break; + } + istring[istring_index] = '\0'; + } + else + strip_trailing (istring, istring_index - 1, 1); + + if (rflag) + *rflag = tflag; + return istring; +} + +/* Perform command substitution on STRING. This returns a WORD_DESC * with the + contained string possibly quoted. */ +WORD_DESC * +command_substitute (string, quoted) + char *string; + int quoted; +{ + pid_t pid, old_pid, old_pipeline_pgrp, old_async_pid; + char *istring; + int result, fildes[2], function_value, pflags, rc, tflag; + WORD_DESC *ret; + + istring = (char *)NULL; + + /* Don't fork () if there is no need to. In the case of no command to + run, just return NULL. */ + if (!string || !*string || (string[0] == '\n' && !string[1])) + return ((WORD_DESC *)NULL); + + if (wordexp_only && read_but_dont_execute) + { + last_command_exit_value = EX_WEXPCOMSUB; + jump_to_top_level (EXITPROG); + } + + /* We're making the assumption here that the command substitution will + eventually run a command from the file system. Since we'll run + maybe_make_export_env in this subshell before executing that command, + the parent shell and any other shells it starts will have to remake + the environment. If we make it before we fork, other shells won't + have to. Don't bother if we have any temporary variable assignments, + though, because the export environment will be remade after this + command completes anyway, but do it if all the words to be expanded + are variable assignments. */ + if (subst_assign_varlist == 0 || garglist == 0) + maybe_make_export_env (); /* XXX */ + + /* Flags to pass to parse_and_execute() */ + pflags = (interactive && sourcelevel == 0) ? SEVAL_RESETLINE : 0; + + /* Pipe the output of executing STRING into the current shell. */ + if (pipe (fildes) < 0) + { + sys_error (_("cannot make pipe for command substitution")); + goto error_exit; + } + + old_pid = last_made_pid; +#if defined (JOB_CONTROL) + old_pipeline_pgrp = pipeline_pgrp; + /* Don't reset the pipeline pgrp if we're already a subshell in a pipeline. */ + if ((subshell_environment & SUBSHELL_PIPE) == 0) + pipeline_pgrp = shell_pgrp; + cleanup_the_pipeline (); +#endif /* JOB_CONTROL */ + + old_async_pid = last_asynchronous_pid; + pid = make_child ((char *)NULL, subshell_environment&SUBSHELL_ASYNC); + last_asynchronous_pid = old_async_pid; + + if (pid == 0) + { + /* Reset the signal handlers in the child, but don't free the + trap strings. Set a flag noting that we have to free the + trap strings if we run trap to change a signal disposition. */ + reset_signal_handlers (); + subshell_environment |= SUBSHELL_RESETTRAP; + } + +#if defined (JOB_CONTROL) + /* XXX DO THIS ONLY IN PARENT ? XXX */ + set_sigchld_handler (); + stop_making_children (); + if (pid != 0) + pipeline_pgrp = old_pipeline_pgrp; +#else + stop_making_children (); +#endif /* JOB_CONTROL */ + + if (pid < 0) + { + sys_error (_("cannot make child for command substitution")); + error_exit: + + FREE (istring); + close (fildes[0]); + close (fildes[1]); + return ((WORD_DESC *)NULL); + } + + if (pid == 0) + { + set_sigint_handler (); /* XXX */ + + free_pushed_string_input (); + + if (dup2 (fildes[1], 1) < 0) + { + sys_error (_("command_substitute: cannot duplicate pipe as fd 1")); + exit (EXECUTION_FAILURE); + } + + /* If standard output is closed in the parent shell + (such as after `exec >&-'), file descriptor 1 will be + the lowest available file descriptor, and end up in + fildes[0]. This can happen for stdin and stderr as well, + but stdout is more important -- it will cause no output + to be generated from this command. */ + if ((fildes[1] != fileno (stdin)) && + (fildes[1] != fileno (stdout)) && + (fildes[1] != fileno (stderr))) + close (fildes[1]); + + if ((fildes[0] != fileno (stdin)) && + (fildes[0] != fileno (stdout)) && + (fildes[0] != fileno (stderr))) + close (fildes[0]); + + /* The currently executing shell is not interactive. */ + interactive = 0; + + /* This is a subshell environment. */ + subshell_environment |= SUBSHELL_COMSUB; + + /* When not in POSIX mode, command substitution does not inherit + the -e flag. */ + if (posixly_correct == 0) + exit_immediately_on_error = 0; + + remove_quoted_escapes (string); + + startup_state = 2; /* see if we can avoid a fork */ + /* Give command substitution a place to jump back to on failure, + so we don't go back up to main (). */ + result = setjmp (top_level); + + /* If we're running a command substitution inside a shell function, + trap `return' so we don't return from the function in the subshell + and go off to never-never land. */ + if (result == 0 && return_catch_flag) + function_value = setjmp (return_catch); + else + function_value = 0; + + if (result == ERREXIT) + rc = last_command_exit_value; + else if (result == EXITPROG) + rc = last_command_exit_value; + else if (result) + rc = EXECUTION_FAILURE; + else if (function_value) + rc = return_catch_value; + else + { + subshell_level++; + rc = parse_and_execute (string, "command substitution", pflags|SEVAL_NOHIST); + subshell_level--; + } + + last_command_exit_value = rc; + rc = run_exit_trap (); +#if defined (PROCESS_SUBSTITUTION) + unlink_fifo_list (); +#endif + exit (rc); + } + else + { +#if defined (JOB_CONTROL) && defined (PGRP_PIPE) + close_pgrp_pipe (); +#endif /* JOB_CONTROL && PGRP_PIPE */ + + close (fildes[1]); + + tflag = 0; + istring = read_comsub (fildes[0], quoted, &tflag); + + close (fildes[0]); + + current_command_subst_pid = pid; + last_command_exit_value = wait_for (pid); + last_command_subst_pid = pid; + last_made_pid = old_pid; + +#if defined (JOB_CONTROL) + /* If last_command_exit_value > 128, then the substituted command + was terminated by a signal. If that signal was SIGINT, then send + SIGINT to ourselves. This will break out of loops, for instance. */ + if (last_command_exit_value == (128 + SIGINT) && last_command_exit_signal == SIGINT) + kill (getpid (), SIGINT); + + /* wait_for gives the terminal back to shell_pgrp. If some other + process group should have it, give it away to that group here. + pipeline_pgrp is non-zero only while we are constructing a + pipline, so what we are concerned about is whether or not that + pipeline was started in the background. A pipeline started in + the background should never get the tty back here. */ + if (interactive && pipeline_pgrp != (pid_t)0 && (subshell_environment & SUBSHELL_ASYNC) == 0) + give_terminal_to (pipeline_pgrp, 0); +#endif /* JOB_CONTROL */ + + ret = alloc_word_desc (); + ret->word = istring; + ret->flags = tflag; + + return ret; + } +} + +/******************************************************** + * * + * Utility functions for parameter expansion * + * * + ********************************************************/ + +#if defined (ARRAY_VARS) + +static arrayind_t +array_length_reference (s) + char *s; +{ + int len; + arrayind_t ind; + char *akey; + char *t, c; + ARRAY *array; + HASH_TABLE *h; + SHELL_VAR *var; + + var = array_variable_part (s, &t, &len); + + /* If unbound variables should generate an error, report one and return + failure. */ + if ((var == 0 || (assoc_p (var) == 0 && array_p (var) == 0)) && unbound_vars_is_error) + { + c = *--t; + *t = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (s); + *t = c; + return (-1); + } + else if (var == 0) + return 0; + + /* We support a couple of expansions for variables that are not arrays. + We'll return the length of the value for v[0], and 1 for v[@] or + v[*]. Return 0 for everything else. */ + + array = array_p (var) ? array_cell (var) : (ARRAY *)NULL; + h = assoc_p (var) ? assoc_cell (var) : (HASH_TABLE *)NULL; + + if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']') + { + if (assoc_p (var)) + return (h ? assoc_num_elements (h) : 0); + else if (array_p (var)) + return (array ? array_num_elements (array) : 0); + else + return (var_isset (var) ? 1 : 0); + } + + if (assoc_p (var)) + { + t[len - 1] = '\0'; + akey = expand_assignment_string_to_string (t, 0); /* [ */ + t[len - 1] = ']'; + if (akey == 0 || *akey == 0) + { + err_badarraysub (t); + return (-1); + } + t = assoc_reference (assoc_cell (var), akey); + } + else + { + ind = array_expand_index (t, len); + if (ind < 0) + { + err_badarraysub (t); + return (-1); + } + if (array_p (var)) + t = array_reference (array, ind); + else + t = (ind == 0) ? value_cell (var) : (char *)NULL; + } + + len = MB_STRLEN (t); + return (len); +} +#endif /* ARRAY_VARS */ + +static int +valid_brace_expansion_word (name, var_is_special) + char *name; + int var_is_special; +{ + if (DIGIT (*name) && all_digits (name)) + return 1; + else if (var_is_special) + return 1; +#if defined (ARRAY_VARS) + else if (valid_array_reference (name)) + return 1; +#endif /* ARRAY_VARS */ + else if (legal_identifier (name)) + return 1; + else + return 0; +} + +static int +chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at) + char *name; + int quoted; + int *quoted_dollar_atp, *contains_dollar_at; +{ + char *temp1; + + if (name == 0) + { + if (quoted_dollar_atp) + *quoted_dollar_atp = 0; + if (contains_dollar_at) + *contains_dollar_at = 0; + return 0; + } + + /* check for $@ and $* */ + if (name[0] == '@' && name[1] == 0) + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } + else if (name[0] == '*' && name[1] == '\0' && quoted == 0) + { + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } + + /* Now check for ${array[@]} and ${array[*]} */ +#if defined (ARRAY_VARS) + else if (valid_array_reference (name)) + { + temp1 = mbschr (name, '['); + if (temp1 && temp1[1] == '@' && temp1[2] == ']') + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } /* [ */ + /* ${array[*]}, when unquoted, should be treated like ${array[@]}, + which should result in separate words even when IFS is unset. */ + if (temp1 && temp1[1] == '*' && temp1[2] == ']' && quoted == 0) + { + if (contains_dollar_at) + *contains_dollar_at = 1; + return 1; + } + } +#endif + return 0; +} + +/* Parameter expand NAME, and return a new string which is the expansion, + or NULL if there was no expansion. + VAR_IS_SPECIAL is non-zero if NAME is one of the special variables in + the shell, e.g., "@", "$", "*", etc. QUOTED, if non-zero, means that + NAME was found inside of a double-quoted expression. */ +static WORD_DESC * +parameter_brace_expand_word (name, var_is_special, quoted, pflags, indp) + char *name; + int var_is_special, quoted, pflags; + arrayind_t *indp; +{ + WORD_DESC *ret; + char *temp, *tt; + intmax_t arg_index; + SHELL_VAR *var; + int atype, rflags; + arrayind_t ind; + + ret = 0; + temp = 0; + rflags = 0; + + if (indp) + *indp = INTMAX_MIN; + + /* Handle multiple digit arguments, as in ${11}. */ + if (legal_number (name, &arg_index)) + { + tt = get_dollar_var_value (arg_index); + if (tt) + temp = (*tt && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (tt) + : quote_escapes (tt); + else + temp = (char *)NULL; + FREE (tt); + } + else if (var_is_special) /* ${@} */ + { + int sindex; + tt = (char *)xmalloc (2 + strlen (name)); + tt[sindex = 0] = '$'; + strcpy (tt + 1, name); + + ret = param_expand (tt, &sindex, quoted, (int *)NULL, (int *)NULL, + (int *)NULL, (int *)NULL, pflags); + free (tt); + } +#if defined (ARRAY_VARS) + else if (valid_array_reference (name)) + { + temp = array_value (name, quoted, 0, &atype, &ind); + if (atype == 0 && temp) + { + temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (temp) + : quote_escapes (temp); + rflags |= W_ARRAYIND; + if (indp) + *indp = ind; + } + else if (atype == 1 && temp && QUOTED_NULL (temp) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + rflags |= W_HASQUOTEDNULL; + } +#endif + else if (var = find_variable (name)) + { + if (var_isset (var) && invisible_p (var) == 0) + { +#if defined (ARRAY_VARS) + if (assoc_p (var)) + temp = assoc_reference (assoc_cell (var), "0"); + else if (array_p (var)) + temp = array_reference (array_cell (var), 0); + else + temp = value_cell (var); +#else + temp = value_cell (var); +#endif + + if (temp) + temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (temp) + : quote_escapes (temp); + } + else + temp = (char *)NULL; + } + else + temp = (char *)NULL; + + if (ret == 0) + { + ret = alloc_word_desc (); + ret->word = temp; + ret->flags |= rflags; + } + return ret; +} + +/* Expand an indirect reference to a variable: ${!NAME} expands to the + value of the variable whose name is the value of NAME. */ +static WORD_DESC * +parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at) + char *name; + int var_is_special, quoted; + int *quoted_dollar_atp, *contains_dollar_at; +{ + char *temp, *t; + WORD_DESC *w; + + w = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND, 0); + t = w->word; + /* Have to dequote here if necessary */ + if (t) + { + temp = (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + ? dequote_string (t) + : dequote_escapes (t); + free (t); + t = temp; + } + dispose_word_desc (w); + + chk_atstar (t, quoted, quoted_dollar_atp, contains_dollar_at); + if (t == 0) + return (WORD_DESC *)NULL; + + w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0, 0); + free (t); + + return w; +} + +/* Expand the right side of a parameter expansion of the form ${NAMEcVALUE}, + depending on the value of C, the separating character. C can be one of + "-", "+", or "=". QUOTED is true if the entire brace expression occurs + between double quotes. */ +static WORD_DESC * +parameter_brace_expand_rhs (name, value, c, quoted, qdollaratp, hasdollarat) + char *name, *value; + int c, quoted, *qdollaratp, *hasdollarat; +{ + WORD_DESC *w; + WORD_LIST *l; + char *t, *t1, *temp; + int hasdol; + + /* If the entire expression is between double quotes, we want to treat + the value as a double-quoted string, with the exception that we strip + embedded unescaped double quotes (for sh backwards compatibility). */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && *value) + { + hasdol = 0; + temp = string_extract_double_quoted (value, &hasdol, 1); + } + else + temp = value; + + w = alloc_word_desc (); + hasdol = 0; + /* XXX was 0 not quoted */ + l = *temp ? expand_string_for_rhs (temp, quoted, &hasdol, (int *)NULL) + : (WORD_LIST *)0; + if (hasdollarat) + *hasdollarat = hasdol || (l && l->next); + if (temp != value) + free (temp); + if (l) + { + /* The expansion of TEMP returned something. We need to treat things + slightly differently if HASDOL is non-zero. If we have "$@", the + individual words have already been quoted. We need to turn them + into a string with the words separated by the first character of + $IFS without any additional quoting, so string_list_dollar_at won't + do the right thing. We use string_list_dollar_star instead. */ + temp = (hasdol || l->next) ? string_list_dollar_star (l) : string_list (l); + + /* If l->next is not null, we know that TEMP contained "$@", since that + is the only expansion that creates more than one word. */ + if (qdollaratp && ((hasdol && quoted) || l->next)) + *qdollaratp = 1; + dispose_words (l); + } + else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && hasdol) + { + /* The brace expansion occurred between double quotes and there was + a $@ in TEMP. It does not matter if the $@ is quoted, as long as + it does not expand to anything. In this case, we want to return + a quoted empty string. */ + temp = make_quoted_char ('\0'); + w->flags |= W_HASQUOTEDNULL; + } + else + temp = (char *)NULL; + + if (c == '-' || c == '+') + { + w->word = temp; + return w; + } + + /* c == '=' */ + t = temp ? savestring (temp) : savestring (""); + t1 = dequote_string (t); + free (t); +#if defined (ARRAY_VARS) + if (valid_array_reference (name)) + assign_array_element (name, t1, 0); + else +#endif /* ARRAY_VARS */ + bind_variable (name, t1, 0); + + /* From Posix group discussion Feb-March 2010. Issue 7 0000221 */ + free (temp); + + w->word = t1; + return w; +} + +/* Deal with the right hand side of a ${name:?value} expansion in the case + that NAME is null or not set. If VALUE is non-null it is expanded and + used as the error message to print, otherwise a standard message is + printed. */ +static void +parameter_brace_expand_error (name, value) + char *name, *value; +{ + WORD_LIST *l; + char *temp; + + if (value && *value) + { + l = expand_string (value, 0); + temp = string_list (l); + report_error ("%s: %s", name, temp ? temp : ""); /* XXX was value not "" */ + FREE (temp); + dispose_words (l); + } + else + report_error (_("%s: parameter null or not set"), name); + + /* Free the data we have allocated during this expansion, since we + are about to longjmp out. */ + free (name); + FREE (value); +} + +/* Return 1 if NAME is something for which parameter_brace_expand_length is + OK to do. */ +static int +valid_length_expression (name) + char *name; +{ + return (name[1] == '\0' || /* ${#} */ + ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0') || /* special param */ + (DIGIT (name[1]) && all_digits (name + 1)) || /* ${#11} */ +#if defined (ARRAY_VARS) + valid_array_reference (name + 1) || /* ${#a[7]} */ +#endif + legal_identifier (name + 1)); /* ${#PS1} */ +} + +#if defined (HANDLE_MULTIBYTE) +size_t +mbstrlen (s) + const char *s; +{ + size_t clen, nc; + mbstate_t mbs, mbsbak; + + nc = 0; + memset (&mbs, 0, sizeof (mbs)); + mbsbak = mbs; + while ((clen = mbrlen(s, MB_CUR_MAX, &mbs)) != 0) + { + if (MB_INVALIDCH(clen)) + { + clen = 1; /* assume single byte */ + mbs = mbsbak; + } + + s += clen; + nc++; + mbsbak = mbs; + } + return nc; +} +#endif + + +/* Handle the parameter brace expansion that requires us to return the + length of a parameter. */ +static intmax_t +parameter_brace_expand_length (name) + char *name; +{ + char *t, *newname; + intmax_t number, arg_index; + WORD_LIST *list; +#if defined (ARRAY_VARS) + SHELL_VAR *var; +#endif + + if (name[1] == '\0') /* ${#} */ + number = number_of_args (); + else if ((name[1] == '@' || name[1] == '*') && name[2] == '\0') /* ${#@}, ${#*} */ + number = number_of_args (); + else if ((sh_syntaxtab[(unsigned char) name[1]] & CSPECVAR) && name[2] == '\0') + { + /* Take the lengths of some of the shell's special parameters. */ + switch (name[1]) + { + case '-': + t = which_set_flags (); + break; + case '?': + t = itos (last_command_exit_value); + break; + case '$': + t = itos (dollar_dollar_pid); + break; + case '!': + if (last_asynchronous_pid == NO_PID) + t = (char *)NULL; + else + t = itos (last_asynchronous_pid); + break; + case '#': + t = itos (number_of_args ()); + break; + } + number = STRLEN (t); + FREE (t); + } +#if defined (ARRAY_VARS) + else if (valid_array_reference (name + 1)) + number = array_length_reference (name + 1); +#endif /* ARRAY_VARS */ + else + { + number = 0; + + if (legal_number (name + 1, &arg_index)) /* ${#1} */ + { + t = get_dollar_var_value (arg_index); + number = MB_STRLEN (t); + FREE (t); + } +#if defined (ARRAY_VARS) + else if ((var = find_variable (name + 1)) && (invisible_p (var) == 0) && (array_p (var) || assoc_p (var))) + { + if (assoc_p (var)) + t = assoc_reference (assoc_cell (var), "0"); + else + t = array_reference (array_cell (var), 0); + number = MB_STRLEN (t); + } +#endif + else /* ${#PS1} */ + { + newname = savestring (name); + newname[0] = '$'; + list = expand_string (newname, Q_DOUBLE_QUOTES); + t = list ? string_list (list) : (char *)NULL; + free (newname); + if (list) + dispose_words (list); + + number = MB_STRLEN (t); + FREE (t); + } + } + + return (number); +} + +/* Skip characters in SUBSTR until DELIM. SUBSTR is an arithmetic expression, + so we do some ad-hoc parsing of an arithmetic expression to find + the first DELIM, instead of using strchr(3). Two rules: + 1. If the substring contains a `(', read until closing `)'. + 2. If the substring contains a `?', read past one `:' for each `?'. +*/ + +static char * +skiparith (substr, delim) + char *substr; + int delim; +{ + size_t sublen; + int skipcol, pcount, i; + DECLARE_MBSTATE; + + sublen = strlen (substr); + i = skipcol = pcount = 0; + while (substr[i]) + { + /* Balance parens */ + if (substr[i] == LPAREN) + { + pcount++; + i++; + continue; + } + if (substr[i] == RPAREN && pcount) + { + pcount--; + i++; + continue; + } + if (pcount) + { + ADVANCE_CHAR (substr, sublen, i); + continue; + } + + /* Skip one `:' for each `?' */ + if (substr[i] == ':' && skipcol) + { + skipcol--; + i++; + continue; + } + if (substr[i] == delim) + break; + if (substr[i] == '?') + { + skipcol++; + i++; + continue; + } + ADVANCE_CHAR (substr, sublen, i); + } + + return (substr + i); +} + +/* Verify and limit the start and end of the desired substring. If + VTYPE == 0, a regular shell variable is being used; if it is 1, + then the positional parameters are being used; if it is 2, then + VALUE is really a pointer to an array variable that should be used. + Return value is 1 if both values were OK, 0 if there was a problem + with an invalid expression, or -1 if the values were out of range. */ +static int +verify_substring_values (v, value, substr, vtype, e1p, e2p) + SHELL_VAR *v; + char *value, *substr; + int vtype; + intmax_t *e1p, *e2p; +{ + char *t, *temp1, *temp2; + arrayind_t len; + int expok; +#if defined (ARRAY_VARS) + ARRAY *a; + HASH_TABLE *h; +#endif + + /* duplicate behavior of strchr(3) */ + t = skiparith (substr, ':'); + if (*t && *t == ':') + *t = '\0'; + else + t = (char *)0; + + temp1 = expand_arith_string (substr, Q_DOUBLE_QUOTES); + *e1p = evalexp (temp1, &expok); + free (temp1); + if (expok == 0) + return (0); + + len = -1; /* paranoia */ + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + len = MB_STRLEN (value); + break; + case VT_POSPARMS: + len = number_of_args () + 1; + if (*e1p == 0) + len++; /* add one arg if counting from $0 */ + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + /* For arrays, the first value deals with array indices. Negative + offsets count from one past the array's maximum index. Associative + arrays treat the number of elements as the maximum index. */ + if (assoc_p (v)) + { + h = assoc_cell (v); + len = assoc_num_elements (h) + (*e1p < 0); + } + else + { + a = (ARRAY *)value; + len = array_max_index (a) + (*e1p < 0); /* arrays index from 0 to n - 1 */ + } + break; +#endif + } + + if (len == -1) /* paranoia */ + return -1; + + if (*e1p < 0) /* negative offsets count from end */ + *e1p += len; + + if (*e1p > len || *e1p < 0) + return (-1); + +#if defined (ARRAY_VARS) + /* For arrays, the second offset deals with the number of elements. */ + if (vtype == VT_ARRAYVAR) + len = assoc_p (v) ? assoc_num_elements (h) : array_num_elements (a); +#endif + + if (t) + { + t++; + temp2 = savestring (t); + temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES); + free (temp2); + t[-1] = ':'; + *e2p = evalexp (temp1, &expok); + free (temp1); + if (expok == 0) + return (0); + if ((vtype == VT_ARRAYVAR || vtype == VT_POSPARMS) && *e2p < 0) + { + internal_error (_("%s: substring expression < 0"), t); + return (0); + } +#if defined (ARRAY_VARS) + /* In order to deal with sparse arrays, push the intelligence about how + to deal with the number of elements desired down to the array- + specific functions. */ + if (vtype != VT_ARRAYVAR) +#endif + { + if (*e2p < 0) + { + *e2p += len; + if (*e2p < 0 || *e2p < *e1p) + { + internal_error (_("%s: substring expression < 0"), t); + return (0); + } + } + else + *e2p += *e1p; /* want E2 chars starting at E1 */ + if (*e2p > len) + *e2p = len; + } + } + else + *e2p = len; + + return (1); +} + +/* Return the type of variable specified by VARNAME (simple variable, + positional param, or array variable). Also return the value specified + by VARNAME (value of a variable or a reference to an array element). + QUOTED is the standard description of quoting state, using Q_* defines. + FLAGS is currently a set of flags to pass to array_value. If IND is + non-null and not INTMAX_MIN, and FLAGS includes AV_USEIND, IND is + passed to array_value so the array index is not computed again. + If this returns VT_VARIABLE, the caller assumes that CTLESC and CTLNUL + characters in the value are quoted with CTLESC and takes appropriate + steps. For convenience, *VALP is set to the dequoted VALUE. */ +static int +get_var_and_type (varname, value, ind, quoted, flags, varp, valp) + char *varname, *value; + arrayind_t ind; + int quoted, flags; + SHELL_VAR **varp; + char **valp; +{ + int vtype; + char *temp; +#if defined (ARRAY_VARS) + SHELL_VAR *v; +#endif + arrayind_t lind; + + /* This sets vtype to VT_VARIABLE or VT_POSPARMS */ + vtype = (varname[0] == '@' || varname[0] == '*') && varname[1] == '\0'; + if (vtype == VT_POSPARMS && varname[0] == '*') + vtype |= VT_STARSUB; + *varp = (SHELL_VAR *)NULL; + +#if defined (ARRAY_VARS) + if (valid_array_reference (varname)) + { + v = array_variable_part (varname, &temp, (int *)0); + /* If we want to signal array_value to use an already-computed index, + set LIND to that index */ + lind = (ind != INTMAX_MIN && (flags & AV_USEIND)) ? ind : 0; + if (v && (array_p (v) || assoc_p (v))) + { /* [ */ + if (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']') + { + /* Callers have to differentiate betwen indexed and associative */ + vtype = VT_ARRAYVAR; + if (temp[0] == '*') + vtype |= VT_STARSUB; + *valp = array_p (v) ? (char *)array_cell (v) : (char *)assoc_cell (v); + } + else + { + vtype = VT_ARRAYMEMBER; + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); + } + *varp = v; + } + else if (v && (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']')) + { + vtype = VT_VARIABLE; + *varp = v; + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + *valp = dequote_string (value); + else + *valp = dequote_escapes (value); + } + else + { + vtype = VT_ARRAYMEMBER; + *varp = v; + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); + } + } + else if ((v = find_variable (varname)) && (invisible_p (v) == 0) && (assoc_p (v) || array_p (v))) + { + vtype = VT_ARRAYMEMBER; + *varp = v; + *valp = assoc_p (v) ? assoc_reference (assoc_cell (v), "0") : array_reference (array_cell (v), 0); + } + else +#endif + { + if (value && vtype == VT_VARIABLE) + { + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + *valp = dequote_string (value); + else + *valp = dequote_escapes (value); + } + else + *valp = value; + } + + return vtype; +} + +/******************************************************/ +/* */ +/* Functions to extract substrings of variable values */ +/* */ +/******************************************************/ + +#if defined (HANDLE_MULTIBYTE) +/* Character-oriented rather than strictly byte-oriented substrings. S and + E, rather being strict indices into STRING, indicate character (possibly + multibyte character) positions that require calculation. + Used by the ${param:offset[:length]} expansion. */ +static char * +mb_substring (string, s, e) + char *string; + int s, e; +{ + char *tt; + int start, stop, i, slen; + DECLARE_MBSTATE; + + start = 0; + /* Don't need string length in ADVANCE_CHAR unless multibyte chars possible. */ + slen = (MB_CUR_MAX > 1) ? STRLEN (string) : 0; + + i = s; + while (string[start] && i--) + ADVANCE_CHAR (string, slen, start); + stop = start; + i = e - s; + while (string[stop] && i--) + ADVANCE_CHAR (string, slen, stop); + tt = substring (string, start, stop); + return tt; +} +#endif + +/* Process a variable substring expansion: ${name:e1[:e2]}. If VARNAME + is `@', use the positional parameters; otherwise, use the value of + VARNAME. If VARNAME is an array variable, use the array elements. */ + +static char * +parameter_brace_substring (varname, value, ind, substr, quoted, flags) + char *varname, *value; + int ind; + char *substr; + int quoted, flags; +{ + intmax_t e1, e2; + int vtype, r, starsub; + char *temp, *val, *tt, *oname; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + oname = this_command_name; + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + { + this_command_name = oname; + return ((char *)NULL); + } + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + r = verify_substring_values (v, val, substr, vtype, &e1, &e2); + this_command_name = oname; + if (r <= 0) + return ((r == 0) ? &expand_param_error : (char *)NULL); + + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: +#if defined (HANDLE_MULTIBYTE) + if (MB_CUR_MAX > 1) + tt = mb_substring (val, e1, e2); + else +#endif + tt = substring (val, e1, e2); + + if (vtype == VT_VARIABLE) + FREE (val); + if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) + temp = quote_string (tt); + else + temp = tt ? quote_escapes (tt) : (char *)NULL; + FREE (tt); + break; + case VT_POSPARMS: + tt = pos_params (varname, e1, e2, quoted); + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0) + { + temp = tt ? quote_escapes (tt) : (char *)NULL; + FREE (tt); + } + else + temp = tt; + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + if (assoc_p (v)) + /* we convert to list and take first e2 elements starting at e1th + element -- officially undefined for now */ + temp = assoc_subrange (assoc_cell (v), e1, e2, starsub, quoted); + else + /* We want E2 to be the number of elements desired (arrays can be sparse, + so verify_substring_values just returns the numbers specified and we + rely on array_subrange to understand how to deal with them). */ + temp = array_subrange (array_cell (v), e1, e2, starsub, quoted); + /* array_subrange now calls array_quote_escapes as appropriate, so the + caller no longer needs to. */ + break; +#endif + default: + temp = (char *)NULL; + } + + return temp; +} + +/****************************************************************/ +/* */ +/* Functions to perform pattern substitution on variable values */ +/* */ +/****************************************************************/ + +static int +shouldexp_replacement (s) + char *s; +{ + register char *p; + + for (p = s; p && *p; p++) + { + if (*p == '\\') + p++; + else if (*p == '&') + return 1; + } + return 0; +} + +char * +pat_subst (string, pat, rep, mflags) + char *string, *pat, *rep; + int mflags; +{ + char *ret, *s, *e, *str, *rstr, *mstr; + int rsize, rptr, l, replen, mtype, rxpand, rslen, mlen; + + if (string == 0) + return (savestring ("")); + + mtype = mflags & MATCH_TYPEMASK; + +#if 0 /* bash-4.2 ? */ + rxpand = (rep && *rep) ? shouldexp_replacement (rep) : 0; +#else + rxpand = 0; +#endif + + /* Special cases: + * 1. A null pattern with mtype == MATCH_BEG means to prefix STRING + * with REP and return the result. + * 2. A null pattern with mtype == MATCH_END means to append REP to + * STRING and return the result. + * These don't understand or process `&' in the replacement string. + */ + if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END)) + { + replen = STRLEN (rep); + l = STRLEN (string); + ret = (char *)xmalloc (replen + l + 2); + if (replen == 0) + strcpy (ret, string); + else if (mtype == MATCH_BEG) + { + strcpy (ret, rep); + strcpy (ret + replen, string); + } + else + { + strcpy (ret, string); + strcpy (ret + l, rep); + } + return (ret); + } + + ret = (char *)xmalloc (rsize = 64); + ret[0] = '\0'; + + for (replen = STRLEN (rep), rptr = 0, str = string;;) + { + if (match_pattern (str, pat, mtype, &s, &e) == 0) + break; + l = s - str; + + if (rxpand) + { + int x; + mlen = e - s; + mstr = xmalloc (mlen + 1); + for (x = 0; x < mlen; x++) + mstr[x] = s[x]; + mstr[mlen] = '\0'; + rstr = strcreplace (rep, '&', mstr, 0); + rslen = strlen (rstr); + } + else + { + rstr = rep; + rslen = replen; + } + + RESIZE_MALLOCED_BUFFER (ret, rptr, (l + rslen), rsize, 64); + + /* OK, now copy the leading unmatched portion of the string (from + str to s) to ret starting at rptr (the current offset). Then copy + the replacement string at ret + rptr + (s - str). Increment + rptr (if necessary) and str and go on. */ + if (l) + { + strncpy (ret + rptr, str, l); + rptr += l; + } + if (replen) + { + strncpy (ret + rptr, rstr, rslen); + rptr += rslen; + } + str = e; /* e == end of match */ + + if (rstr != rep) + free (rstr); + + if (((mflags & MATCH_GLOBREP) == 0) || mtype != MATCH_ANY) + break; + + if (s == e) + { + /* On a zero-length match, make sure we copy one character, since + we increment one character to avoid infinite recursion. */ + RESIZE_MALLOCED_BUFFER (ret, rptr, 1, rsize, 64); + ret[rptr++] = *str++; + e++; /* avoid infinite recursion on zero-length match */ + } + } + + /* Now copy the unmatched portion of the input string */ + if (str && *str) + { + RESIZE_MALLOCED_BUFFER (ret, rptr, STRLEN(str) + 1, rsize, 64); + strcpy (ret + rptr, str); + } + else + ret[rptr] = '\0'; + + return ret; +} + +/* Do pattern match and replacement on the positional parameters. */ +static char * +pos_params_pat_subst (string, pat, rep, mflags) + char *string, *pat, *rep; + int mflags; +{ + WORD_LIST *save, *params; + WORD_DESC *w; + char *ret; + int pchar, qflags; + + save = params = list_rest_of_args (); + if (save == 0) + return ((char *)NULL); + + for ( ; params; params = params->next) + { + ret = pat_subst (params->word->word, pat, rep, mflags); + w = alloc_word_desc (); + w->word = ret ? ret : savestring (""); + dispose_word (params->word); + params->word = w; + } + + pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@'; + qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0; + +#if 0 + if ((mflags & (MATCH_QUOTED|MATCH_STARSUB)) == (MATCH_QUOTED|MATCH_STARSUB)) + ret = string_list_dollar_star (quote_list (save)); + else if ((mflags & MATCH_STARSUB) == MATCH_STARSUB) + ret = string_list_dollar_star (save); + else if ((mflags & MATCH_QUOTED) == MATCH_QUOTED) + ret = string_list_dollar_at (save, qflags); + else + ret = string_list_dollar_star (save); +#else + ret = string_list_pos_params (pchar, save, qflags); +#endif + + dispose_words (save); + + return (ret); +} + +/* Perform pattern substitution on VALUE, which is the expansion of + VARNAME. PATSUB is an expression supplying the pattern to match + and the string to substitute. QUOTED is a flags word containing + the type of quoting currently in effect. */ +static char * +parameter_brace_patsub (varname, value, ind, patsub, quoted, flags) + char *varname, *value; + int ind; + char *patsub; + int quoted, flags; +{ + int vtype, mflags, starsub, delim; + char *val, *temp, *pat, *rep, *p, *lpatsub, *tt; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + return ((char *)NULL); + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + mflags = 0; + if (patsub && *patsub == '/') + { + mflags |= MATCH_GLOBREP; + patsub++; + } + + /* Malloc this because expand_string_if_necessary or one of the expansion + functions in its call chain may free it on a substitution error. */ + lpatsub = savestring (patsub); + + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + mflags |= MATCH_QUOTED; + + if (starsub) + mflags |= MATCH_STARSUB; + + /* If the pattern starts with a `/', make sure we skip over it when looking + for the replacement delimiter. */ +#if 0 + if (rep = quoted_strchr ((*patsub == '/') ? lpatsub+1 : lpatsub, '/', ST_BACKSL)) + *rep++ = '\0'; + else + rep = (char *)NULL; +#else + delim = skip_to_delim (lpatsub, ((*patsub == '/') ? 1 : 0), "/", 0); + if (lpatsub[delim] == '/') + { + lpatsub[delim] = 0; + rep = lpatsub + delim + 1; + } + else + rep = (char *)NULL; +#endif + + if (rep && *rep == '\0') + rep = (char *)NULL; + + /* Perform the same expansions on the pattern as performed by the + pattern removal expansions. */ + pat = getpattern (lpatsub, quoted, 1); + + if (rep) + { + if ((mflags & MATCH_QUOTED) == 0) + rep = expand_string_if_necessary (rep, quoted, expand_string_unsplit); + else + rep = expand_string_to_string_internal (rep, quoted, expand_string_unsplit); + } + + /* ksh93 doesn't allow the match specifier to be a part of the expanded + pattern. This is an extension. Make sure we don't anchor the pattern + at the beginning or end of the string if we're doing global replacement, + though. */ + p = pat; + if (mflags & MATCH_GLOBREP) + mflags |= MATCH_ANY; + else if (pat && pat[0] == '#') + { + mflags |= MATCH_BEG; + p++; + } + else if (pat && pat[0] == '%') + { + mflags |= MATCH_END; + p++; + } + else + mflags |= MATCH_ANY; + + /* OK, we now want to substitute REP for PAT in VAL. If + flags & MATCH_GLOBREP is non-zero, the substitution is done + everywhere, otherwise only the first occurrence of PAT is + replaced. The pattern matching code doesn't understand + CTLESC quoting CTLESC and CTLNUL so we use the dequoted variable + values passed in (VT_VARIABLE) so the pattern substitution + code works right. We need to requote special chars after + we're done for VT_VARIABLE and VT_ARRAYMEMBER, and for the + other cases if QUOTED == 0, since the posparams and arrays + indexed by * or @ do special things when QUOTED != 0. */ + + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + temp = pat_subst (val, p, rep, mflags); + if (vtype == VT_VARIABLE) + FREE (val); + if (temp) + { + tt = (mflags & MATCH_QUOTED) ? quote_string (temp) : quote_escapes (temp); + free (temp); + temp = tt; + } + break; + case VT_POSPARMS: + temp = pos_params_pat_subst (val, p, rep, mflags); + if (temp && (mflags & MATCH_QUOTED) == 0) + { + tt = quote_escapes (temp); + free (temp); + temp = tt; + } + break; +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + temp = assoc_p (v) ? assoc_patsub (assoc_cell (v), p, rep, mflags) + : array_patsub (array_cell (v), p, rep, mflags); + /* Don't call quote_escapes anymore; array_patsub calls + array_quote_escapes as appropriate before adding the + space separators; ditto for assoc_patsub. */ + break; +#endif + } + + FREE (pat); + FREE (rep); + free (lpatsub); + + return temp; +} + +/****************************************************************/ +/* */ +/* Functions to perform case modification on variable values */ +/* */ +/****************************************************************/ + +/* Do case modification on the positional parameters. */ + +static char * +pos_params_modcase (string, pat, modop, mflags) + char *string, *pat; + int modop; + int mflags; +{ + WORD_LIST *save, *params; + WORD_DESC *w; + char *ret; + int pchar, qflags; + + save = params = list_rest_of_args (); + if (save == 0) + return ((char *)NULL); + + for ( ; params; params = params->next) + { + ret = sh_modcase (params->word->word, pat, modop); + w = alloc_word_desc (); + w->word = ret ? ret : savestring (""); + dispose_word (params->word); + params->word = w; + } + + pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@'; + qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0; + + ret = string_list_pos_params (pchar, save, qflags); + dispose_words (save); + + return (ret); +} + +/* Perform case modification on VALUE, which is the expansion of + VARNAME. MODSPEC is an expression supplying the type of modification + to perform. QUOTED is a flags word containing the type of quoting + currently in effect. */ +static char * +parameter_brace_casemod (varname, value, ind, modspec, patspec, quoted, flags) + char *varname, *value; + int ind, modspec; + char *patspec; + int quoted, flags; +{ + int vtype, starsub, modop, mflags, x; + char *val, *temp, *pat, *p, *lpat, *tt; + SHELL_VAR *v; + + if (value == 0) + return ((char *)NULL); + + this_command_name = varname; + + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); + if (vtype == -1) + return ((char *)NULL); + + starsub = vtype & VT_STARSUB; + vtype &= ~VT_STARSUB; + + modop = 0; + mflags = 0; + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + mflags |= MATCH_QUOTED; + if (starsub) + mflags |= MATCH_STARSUB; + + p = patspec; + if (modspec == '^') + { + x = p && p[0] == modspec; + modop = x ? CASE_UPPER : CASE_UPFIRST; + p += x; + } + else if (modspec == ',') + { + x = p && p[0] == modspec; + modop = x ? CASE_LOWER : CASE_LOWFIRST; + p += x; + } + else if (modspec == '~') + { + x = p && p[0] == modspec; + modop = x ? CASE_TOGGLEALL : CASE_TOGGLE; + p += x; + } + + lpat = p ? savestring (p) : 0; + /* Perform the same expansions on the pattern as performed by the + pattern removal expansions. FOR LATER */ + pat = lpat ? getpattern (lpat, quoted, 1) : 0; + + /* OK, now we do the case modification. */ + switch (vtype) + { + case VT_VARIABLE: + case VT_ARRAYMEMBER: + temp = sh_modcase (val, pat, modop); + if (vtype == VT_VARIABLE) + FREE (val); + if (temp) + { + tt = (mflags & MATCH_QUOTED) ? quote_string (temp) : quote_escapes (temp); + free (temp); + temp = tt; + } + break; + + case VT_POSPARMS: + temp = pos_params_modcase (val, pat, modop, mflags); + if (temp && (mflags & MATCH_QUOTED) == 0) + { + tt = quote_escapes (temp); + free (temp); + temp = tt; + } + break; + +#if defined (ARRAY_VARS) + case VT_ARRAYVAR: + temp = assoc_p (v) ? assoc_modcase (assoc_cell (v), pat, modop, mflags) + : array_modcase (array_cell (v), pat, modop, mflags); + /* Don't call quote_escapes; array_modcase calls array_quote_escapes + as appropriate before adding the space separators; ditto for + assoc_modcase. */ + break; +#endif + } + + FREE (pat); + free (lpat); + + return temp; +} + +/* Check for unbalanced parens in S, which is the contents of $(( ... )). If + any occur, this must be a nested command substitution, so return 0. + Otherwise, return 1. A valid arithmetic expression must always have a + ( before a matching ), so any cases where there are more right parens + means that this must not be an arithmetic expression, though the parser + will not accept it without a balanced total number of parens. */ +static int +chk_arithsub (s, len) + const char *s; + int len; +{ + int i, count; + DECLARE_MBSTATE; + + i = count = 0; + while (i < len) + { + if (s[i] == LPAREN) + count++; + else if (s[i] == RPAREN) + { + count--; + if (count < 0) + return 0; + } + + switch (s[i]) + { + default: + ADVANCE_CHAR (s, len, i); + break; + + case '\\': + i++; + if (s[i]) + ADVANCE_CHAR (s, len, i); + break; + + case '\'': + i = skip_single_quoted (s, len, ++i); + break; + + case '"': + i = skip_double_quoted ((char *)s, len, ++i); + break; + } + } + + return (count == 0); +} + +/****************************************************************/ +/* */ +/* Functions to perform parameter expansion on a string */ +/* */ +/****************************************************************/ + +/* ${[#][!]name[[:][^[^]][,[,]]#[#]%[%]-=?+[word][:e1[:e2]]]} */ +static WORD_DESC * +parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, contains_dollar_at) + char *string; + int *indexp, quoted, *quoted_dollar_atp, *contains_dollar_at, pflags; +{ + int check_nullness, var_is_set, var_is_null, var_is_special; + int want_substring, want_indir, want_patsub, want_casemod; + char *name, *value, *temp, *temp1; + WORD_DESC *tdesc, *ret; + int t_index, sindex, c, tflag, modspec; + intmax_t number; + arrayind_t ind; + + temp = temp1 = value = (char *)NULL; + var_is_set = var_is_null = var_is_special = check_nullness = 0; + want_substring = want_indir = want_patsub = want_casemod = 0; + + sindex = *indexp; + t_index = ++sindex; + /* ${#var} doesn't have any of the other parameter expansions on it. */ + if (string[t_index] == '#' && legal_variable_starter (string[t_index+1])) /* {{ */ + name = string_extract (string, &t_index, "}", SX_VARNAME); + else +#if defined (CASEMOD_EXPANSIONS) + /* To enable case-toggling expansions using the `~' operator character + change the 1 to 0. */ +# if defined (CASEMOD_CAPCASE) + name = string_extract (string, &t_index, "#%^,~:-=?+/}", SX_VARNAME); +# else + name = string_extract (string, &t_index, "#%^,:-=?+/}", SX_VARNAME); +# endif /* CASEMOD_CAPCASE */ +#else + name = string_extract (string, &t_index, "#%:-=?+/}", SX_VARNAME); +#endif /* CASEMOD_EXPANSIONS */ + + ret = 0; + tflag = 0; + + ind = INTMAX_MIN; + + /* If the name really consists of a special variable, then make sure + that we have the entire name. We don't allow indirect references + to special variables except `#', `?', `@' and `*'. */ + if ((sindex == t_index && + (string[t_index] == '-' || + string[t_index] == '?' || + string[t_index] == '#')) || + (sindex == t_index - 1 && string[sindex] == '!' && + (string[t_index] == '#' || + string[t_index] == '?' || + string[t_index] == '@' || + string[t_index] == '*'))) + { + t_index++; + free (name); + temp1 = string_extract (string, &t_index, "#%:-=?+/}", 0); + name = (char *)xmalloc (3 + (strlen (temp1))); + *name = string[sindex]; + if (string[sindex] == '!') + { + /* indirect reference of $#, $?, $@, or $* */ + name[1] = string[sindex + 1]; + strcpy (name + 2, temp1); + } + else + strcpy (name + 1, temp1); + free (temp1); + } + sindex = t_index; + + /* Find out what character ended the variable name. Then + do the appropriate thing. */ + if (c = string[sindex]) + sindex++; + + /* If c is followed by one of the valid parameter expansion + characters, move past it as normal. If not, assume that + a substring specification is being given, and do not move + past it. */ + if (c == ':' && VALID_PARAM_EXPAND_CHAR (string[sindex])) + { + check_nullness++; + if (c = string[sindex]) + sindex++; + } + else if (c == ':' && string[sindex] != RBRACE) + want_substring = 1; + else if (c == '/' && string[sindex] != RBRACE) + want_patsub = 1; +#if defined (CASEMOD_EXPANSIONS) + else if (c == '^' || c == ',' || c == '~') + { + modspec = c; + want_casemod = 1; + } +#endif + + /* Catch the valid and invalid brace expressions that made it through the + tests above. */ + /* ${#-} is a valid expansion and means to take the length of $-. + Similarly for ${#?} and ${##}... */ + if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 && + VALID_SPECIAL_LENGTH_PARAM (c) && string[sindex] == RBRACE) + { + name = (char *)xrealloc (name, 3); + name[1] = c; + name[2] = '\0'; + c = string[sindex++]; + } + + /* ...but ${#%}, ${#:}, ${#=}, ${#+}, and ${#/} are errors. */ + if (name[0] == '#' && name[1] == '\0' && check_nullness == 0 && + member (c, "%:=+/") && string[sindex] == RBRACE) + { + temp = (char *)NULL; + goto bad_substitution; + } + + /* Indirect expansion begins with a `!'. A valid indirect expansion is + either a variable name, one of the positional parameters or a special + variable that expands to one of the positional parameters. */ + want_indir = *name == '!' && + (legal_variable_starter ((unsigned char)name[1]) || DIGIT (name[1]) + || VALID_INDIR_PARAM (name[1])); + + /* Determine the value of this variable. */ + + /* Check for special variables, directly referenced. */ + if (SPECIAL_VAR (name, want_indir)) + var_is_special++; + + /* Check for special expansion things, like the length of a parameter */ + if (*name == '#' && name[1]) + { + /* If we are not pointing at the character just after the + closing brace, then we haven't gotten all of the name. + Since it begins with a special character, this is a bad + substitution. Also check NAME for validity before trying + to go on. */ + if (string[sindex - 1] != RBRACE || (valid_length_expression (name) == 0)) + { + temp = (char *)NULL; + goto bad_substitution; + } + + number = parameter_brace_expand_length (name); + free (name); + + *indexp = sindex; + if (number < 0) + return (&expand_wdesc_error); + else + { + ret = alloc_word_desc (); + ret->word = itos (number); + return ret; + } + } + + /* ${@} is identical to $@. */ + if (name[0] == '@' && name[1] == '\0') + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + + if (contains_dollar_at) + *contains_dollar_at = 1; + } + + /* Process ${!PREFIX*} expansion. */ + if (want_indir && string[sindex - 1] == RBRACE && + (string[sindex - 2] == '*' || string[sindex - 2] == '@') && + legal_variable_starter ((unsigned char) name[1])) + { + char **x; + WORD_LIST *xlist; + + temp1 = savestring (name + 1); + number = strlen (temp1); + temp1[number - 1] = '\0'; + x = all_variables_matching_prefix (temp1); + xlist = strvec_to_word_list (x, 0, 0); + if (string[sindex - 2] == '*') + temp = string_list_dollar_star (xlist); + else + { + temp = string_list_dollar_at (xlist, quoted); + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + } + free (x); + dispose_words (xlist); + free (temp1); + *indexp = sindex; + + ret = alloc_word_desc (); + ret->word = temp; + return ret; + } + +#if defined (ARRAY_VARS) + /* Process ${!ARRAY[@]} and ${!ARRAY[*]} expansion. */ /* [ */ + if (want_indir && string[sindex - 1] == RBRACE && + string[sindex - 2] == ']' && valid_array_reference (name+1)) + { + char *x, *x1; + + temp1 = savestring (name + 1); + x = array_variable_name (temp1, &x1, (int *)0); /* [ */ + FREE (x); + if (ALL_ELEMENT_SUB (x1[0]) && x1[1] == ']') + { + temp = array_keys (temp1, quoted); /* handles assoc vars too */ + if (x1[0] == '@') + { + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + } + + free (temp1); + *indexp = sindex; + + ret = alloc_word_desc (); + ret->word = temp; + return ret; + } + + free (temp1); + } +#endif /* ARRAY_VARS */ + + /* Make sure that NAME is valid before trying to go on. */ + if (valid_brace_expansion_word (want_indir ? name + 1 : name, + var_is_special) == 0) + { + temp = (char *)NULL; + goto bad_substitution; + } + + if (want_indir) + tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at); + else + tdesc = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND|(pflags&PF_NOSPLIT2), &ind); + + if (tdesc) + { + temp = tdesc->word; + tflag = tdesc->flags; + dispose_word_desc (tdesc); + } + else + temp = (char *)0; + +#if defined (ARRAY_VARS) + if (valid_array_reference (name)) + chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at); +#endif + + var_is_set = temp != (char *)0; + var_is_null = check_nullness && (var_is_set == 0 || *temp == 0); + + /* Get the rest of the stuff inside the braces. */ + if (c && c != RBRACE) + { + /* Extract the contents of the ${ ... } expansion + according to the Posix.2 rules. */ + value = extract_dollar_brace_string (string, &sindex, quoted, (c == '%' || c == '#') ? SX_POSIXEXP : 0); + if (string[sindex] == RBRACE) + sindex++; + else + goto bad_substitution; + } + else + value = (char *)NULL; + + *indexp = sindex; + + /* If this is a substring spec, process it and add the result. */ + if (want_substring) + { + temp1 = parameter_brace_substring (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + FREE (name); + FREE (value); + FREE (temp); + + if (temp1 == &expand_param_error) + return (&expand_wdesc_error); + else if (temp1 == &expand_param_fatal) + return (&expand_wdesc_fatal); + + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + } + else if (want_patsub) + { + temp1 = parameter_brace_patsub (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + FREE (name); + FREE (value); + FREE (temp); + + if (temp1 == &expand_param_error) + return (&expand_wdesc_error); + else if (temp1 == &expand_param_fatal) + return (&expand_wdesc_fatal); + + ret = alloc_word_desc (); + ret->word = temp1; + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + } +#if defined (CASEMOD_EXPANSIONS) + else if (want_casemod) + { + temp1 = parameter_brace_casemod (name, temp, ind, modspec, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + FREE (name); + FREE (value); + FREE (temp); + + if (temp1 == &expand_param_error) + return (&expand_wdesc_error); + else if (temp1 == &expand_param_fatal) + return (&expand_wdesc_fatal); + + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + } +#endif + + /* Do the right thing based on which character ended the variable name. */ + switch (c) + { + default: + case '\0': + bad_substitution: + report_error (_("%s: bad substitution"), string ? string : "??"); + FREE (value); + FREE (temp); + free (name); + return &expand_wdesc_error; + + case RBRACE: + if (var_is_set == 0 && unbound_vars_is_error && ((name[0] != '@' && name[0] != '*') || name[1])) + { + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (name); + FREE (value); + FREE (temp); + free (name); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + break; + + case '#': /* ${param#[#]pattern} */ + case '%': /* ${param%[%]pattern} */ + if (value == 0 || *value == '\0' || temp == 0 || *temp == '\0') + { + FREE (value); + break; + } + temp1 = parameter_brace_remove_pattern (name, temp, ind, value, c, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); + free (temp); + free (value); + free (name); + + ret = alloc_word_desc (); + ret->word = temp1; + if (temp1 && QUOTED_NULL (temp1) && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ret->flags |= W_QUOTED|W_HASQUOTEDNULL; + return ret; + + case '-': + case '=': + case '?': + case '+': + if (var_is_set && var_is_null == 0) + { + /* If the operator is `+', we don't want the value of the named + variable for anything, just the value of the right hand side. */ + if (c == '+') + { + /* XXX -- if we're double-quoted and the named variable is "$@", + we want to turn off any special handling of "$@" -- + we're not using it, so whatever is on the rhs applies. */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 0; + if (contains_dollar_at) + *contains_dollar_at = 0; + + FREE (temp); + if (value) + { + /* From Posix discussion on austin-group list. Issue 221 + requires that backslashes escaping `}' inside + double-quoted ${...} be removed. */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + quoted |= Q_DOLBRACE; + ret = parameter_brace_expand_rhs (name, value, c, + quoted, + quoted_dollar_atp, + contains_dollar_at); + /* XXX - fix up later, esp. noting presence of + W_HASQUOTEDNULL in ret->flags */ + free (value); + } + else + temp = (char *)NULL; + } + else + { + FREE (value); + } + /* Otherwise do nothing; just use the value in TEMP. */ + } + else /* VAR not set or VAR is NULL. */ + { + FREE (temp); + temp = (char *)NULL; + if (c == '=' && var_is_special) + { + report_error (_("$%s: cannot assign in this way"), name); + free (name); + free (value); + return &expand_wdesc_error; + } + else if (c == '?') + { + parameter_brace_expand_error (name, value); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + else if (c != '+') + { + /* XXX -- if we're double-quoted and the named variable is "$@", + we want to turn off any special handling of "$@" -- + we're not using it, so whatever is on the rhs applies. */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && quoted_dollar_atp) + *quoted_dollar_atp = 0; + if (contains_dollar_at) + *contains_dollar_at = 0; + + /* From Posix discussion on austin-group list. Issue 221 requires + that backslashes escaping `}' inside double-quoted ${...} be + removed. */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + quoted |= Q_DOLBRACE; + ret = parameter_brace_expand_rhs (name, value, c, quoted, + quoted_dollar_atp, + contains_dollar_at); + /* XXX - fix up later, esp. noting presence of + W_HASQUOTEDNULL in tdesc->flags */ + } + free (value); + } + + break; + } + free (name); + + if (ret == 0) + { + ret = alloc_word_desc (); + ret->flags = tflag; + ret->word = temp; + } + return (ret); +} + +/* Expand a single ${xxx} expansion. The braces are optional. When + the braces are used, parameter_brace_expand() does the work, + possibly calling param_expand recursively. */ +static WORD_DESC * +param_expand (string, sindex, quoted, expanded_something, + contains_dollar_at, quoted_dollar_at_p, had_quoted_null_p, + pflags) + char *string; + int *sindex, quoted, *expanded_something, *contains_dollar_at; + int *quoted_dollar_at_p, *had_quoted_null_p, pflags; +{ + char *temp, *temp1, uerror[3]; + int zindex, t_index, expok; + unsigned char c; + intmax_t number; + SHELL_VAR *var; + WORD_LIST *list; + WORD_DESC *tdesc, *ret; + int tflag; + + zindex = *sindex; + c = string[++zindex]; + + temp = (char *)NULL; + ret = tdesc = (WORD_DESC *)NULL; + tflag = 0; + + /* Do simple cases first. Switch on what follows '$'. */ + switch (c) + { + /* $0 .. $9? */ + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + temp1 = dollar_vars[TODIGIT (c)]; + if (unbound_vars_is_error && temp1 == (char *)NULL) + { + uerror[0] = '$'; + uerror[1] = c; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + if (temp1) + temp = (*temp1 && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ? quote_string (temp1) + : quote_escapes (temp1); + else + temp = (char *)NULL; + + break; + + /* $$ -- pid of the invoking shell. */ + case '$': + temp = itos (dollar_dollar_pid); + break; + + /* $# -- number of positional parameters. */ + case '#': + temp = itos (number_of_args ()); + break; + + /* $? -- return value of the last synchronous command. */ + case '?': + temp = itos (last_command_exit_value); + break; + + /* $- -- flags supplied to the shell on invocation or by `set'. */ + case '-': + temp = which_set_flags (); + break; + + /* $! -- Pid of the last asynchronous command. */ + case '!': + /* If no asynchronous pids have been created, expand to nothing. + If `set -u' has been executed, and no async processes have + been created, this is an expansion error. */ + if (last_asynchronous_pid == NO_PID) + { + if (expanded_something) + *expanded_something = 0; + temp = (char *)NULL; + if (unbound_vars_is_error) + { + uerror[0] = '$'; + uerror[1] = c; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } + } + else + temp = itos (last_asynchronous_pid); + break; + + /* The only difference between this and $@ is when the arg is quoted. */ + case '*': /* `$*' */ + list = list_rest_of_args (); + +#if 0 + /* According to austin-group posix proposal by Geoff Clare in + <20090505091501.GA10097@squonk.masqnet> of 5 May 2009: + + "The shell shall write a message to standard error and + immediately exit when it tries to expand an unset parameter + other than the '@' and '*' special parameters." + */ + + if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0) + { + uerror[0] = '$'; + uerror[1] = '*'; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } +#endif + + /* If there are no command-line arguments, this should just + disappear if there are other characters in the expansion, + even if it's quoted. */ + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && list == 0) + temp = (char *)NULL; + else if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES|Q_PATQUOTE)) + { + /* If we have "$*" we want to make a string of the positional + parameters, separated by the first character of $IFS, and + quote the whole string, including the separators. If IFS + is unset, the parameters are separated by ' '; if $IFS is + null, the parameters are concatenated. */ + temp = (quoted & (Q_DOUBLE_QUOTES|Q_PATQUOTE)) ? string_list_dollar_star (list) : string_list (list); + temp1 = quote_string (temp); + if (*temp == 0) + tflag |= W_HASQUOTEDNULL; + free (temp); + temp = temp1; + } + else + { + /* We check whether or not we're eventually going to split $* here, + for example when IFS is empty and we are processing the rhs of + an assignment statement. In that case, we don't separate the + arguments at all. Otherwise, if the $* is not quoted it is + identical to $@ */ +#if 1 +# if defined (HANDLE_MULTIBYTE) + if (expand_no_split_dollar_star && ifs_firstc[0] == 0) +# else + if (expand_no_split_dollar_star && ifs_firstc == 0) +# endif + temp = string_list_dollar_star (list); + else + temp = string_list_dollar_at (list, quoted); +#else + temp = string_list_dollar_at (list, quoted); +#endif + if (expand_no_split_dollar_star == 0 && contains_dollar_at) + *contains_dollar_at = 1; + } + + dispose_words (list); + break; + + /* When we have "$@" what we want is "$1" "$2" "$3" ... This + means that we have to turn quoting off after we split into + the individually quoted arguments so that the final split + on the first character of $IFS is still done. */ + case '@': /* `$@' */ + list = list_rest_of_args (); + +#if 0 + /* According to austin-group posix proposal by Geoff Clare in + <20090505091501.GA10097@squonk.masqnet> of 5 May 2009: + + "The shell shall write a message to standard error and + immediately exit when it tries to expand an unset parameter + other than the '@' and '*' special parameters." + */ + + if (list == 0 && unbound_vars_is_error && (pflags & PF_IGNUNBOUND) == 0) + { + uerror[0] = '$'; + uerror[1] = '@'; + uerror[2] = '\0'; + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (uerror); + return (interactive_shell ? &expand_wdesc_error : &expand_wdesc_fatal); + } +#endif + + /* We want to flag the fact that we saw this. We can't turn + off quoting entirely, because other characters in the + string might need it (consider "\"$@\""), but we need some + way to signal that the final split on the first character + of $IFS should be done, even though QUOTED is 1. */ + /* XXX - should this test include Q_PATQUOTE? */ + if (quoted_dollar_at_p && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + *quoted_dollar_at_p = 1; + if (contains_dollar_at) + *contains_dollar_at = 1; + +#if 0 + if (pflags & PF_NOSPLIT2) + temp = string_list_internal (quoted ? quote_list (list) : list, " "); + else +#endif + /* We want to separate the positional parameters with the first + character of $IFS in case $IFS is something other than a space. + We also want to make sure that splitting is done no matter what -- + according to POSIX.2, this expands to a list of the positional + parameters no matter what IFS is set to. */ + temp = string_list_dollar_at (list, quoted); + + dispose_words (list); + break; + + case LBRACE: + tdesc = parameter_brace_expand (string, &zindex, quoted, pflags, + quoted_dollar_at_p, + contains_dollar_at); + + if (tdesc == &expand_wdesc_error || tdesc == &expand_wdesc_fatal) + return (tdesc); + temp = tdesc ? tdesc->word : (char *)0; + + /* XXX */ + /* Quoted nulls should be removed if there is anything else + in the string. */ + /* Note that we saw the quoted null so we can add one back at + the end of this function if there are no other characters + in the string, discard TEMP, and go on. The exception to + this is when we have "${@}" and $1 is '', since $@ needs + special handling. */ + if (tdesc && tdesc->word && (tdesc->flags & W_HASQUOTEDNULL) && QUOTED_NULL (temp)) + { + if (had_quoted_null_p) + *had_quoted_null_p = 1; + if (*quoted_dollar_at_p == 0) + { + free (temp); + tdesc->word = temp = (char *)NULL; + } + + } + + ret = tdesc; + goto return0; + + /* Do command or arithmetic substitution. */ + case LPAREN: + /* We have to extract the contents of this paren substitution. */ + t_index = zindex + 1; + temp = extract_command_subst (string, &t_index, 0); + zindex = t_index; + + /* For Posix.2-style `$(( ))' arithmetic substitution, + extract the expression and pass it to the evaluator. */ + if (temp && *temp == LPAREN) + { + char *temp2; + temp1 = temp + 1; + temp2 = savestring (temp1); + t_index = strlen (temp2) - 1; + + if (temp2[t_index] != RPAREN) + { + free (temp2); + goto comsub; + } + + /* Cut off ending `)' */ + temp2[t_index] = '\0'; + + if (chk_arithsub (temp2, t_index) == 0) + { + free (temp2); +#if 0 + internal_warning (_("future versions of the shell will force evaluation as an arithmetic substitution")); +#endif + goto comsub; + } + + /* Expand variables found inside the expression. */ + temp1 = expand_arith_string (temp2, Q_DOUBLE_QUOTES); + free (temp2); + +arithsub: + /* No error messages. */ + this_command_name = (char *)NULL; + number = evalexp (temp1, &expok); + free (temp); + free (temp1); + if (expok == 0) + { + if (interactive_shell == 0 && posixly_correct) + { + last_command_exit_value = EXECUTION_FAILURE; + return (&expand_wdesc_fatal); + } + else + return (&expand_wdesc_error); + } + temp = itos (number); + break; + } + +comsub: + if (pflags & PF_NOCOMSUB) + /* we need zindex+1 because string[zindex] == RPAREN */ + temp1 = substring (string, *sindex, zindex+1); + else + { + tdesc = command_substitute (temp, quoted); + temp1 = tdesc ? tdesc->word : (char *)NULL; + if (tdesc) + dispose_word_desc (tdesc); + } + FREE (temp); + temp = temp1; + break; + + /* Do POSIX.2d9-style arithmetic substitution. This will probably go + away in a future bash release. */ + case '[': + /* Extract the contents of this arithmetic substitution. */ + t_index = zindex + 1; + temp = extract_arithmetic_subst (string, &t_index); + zindex = t_index; + if (temp == 0) + { + temp = savestring (string); + if (expanded_something) + *expanded_something = 0; + goto return0; + } + + /* Do initial variable expansion. */ + temp1 = expand_arith_string (temp, Q_DOUBLE_QUOTES); + + goto arithsub; + + default: + /* Find the variable in VARIABLE_LIST. */ + temp = (char *)NULL; + + for (t_index = zindex; (c = string[zindex]) && legal_variable_char (c); zindex++) + ; + temp1 = (zindex > t_index) ? substring (string, t_index, zindex) : (char *)NULL; + + /* If this isn't a variable name, then just output the `$'. */ + if (temp1 == 0 || *temp1 == '\0') + { + FREE (temp1); + temp = (char *)xmalloc (2); + temp[0] = '$'; + temp[1] = '\0'; + if (expanded_something) + *expanded_something = 0; + goto return0; + } + + /* If the variable exists, return its value cell. */ + var = find_variable (temp1); + + if (var && invisible_p (var) == 0 && var_isset (var)) + { +#if defined (ARRAY_VARS) + if (assoc_p (var) || array_p (var)) + { + temp = array_p (var) ? array_reference (array_cell (var), 0) + : assoc_reference (assoc_cell (var), "0"); + if (temp) + temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ? quote_string (temp) + : quote_escapes (temp); + else if (unbound_vars_is_error) + goto unbound_variable; + } + else +#endif + { + temp = value_cell (var); + + temp = (*temp && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES))) + ? quote_string (temp) + : quote_escapes (temp); + } + + free (temp1); + + goto return0; + } + + temp = (char *)NULL; + +unbound_variable: + if (unbound_vars_is_error) + { + last_command_exit_value = EXECUTION_FAILURE; + err_unboundvar (temp1); + } + else + { + free (temp1); + goto return0; + } + + free (temp1); + last_command_exit_value = EXECUTION_FAILURE; + return ((unbound_vars_is_error && interactive_shell == 0) + ? &expand_wdesc_fatal + : &expand_wdesc_error); + } + + if (string[zindex]) + zindex++; + +return0: + *sindex = zindex; + + if (ret == 0) + { + ret = alloc_word_desc (); + ret->flags = tflag; /* XXX */ + ret->word = temp; + } + return ret; +} + +/* Make a word list which is the result of parameter and variable + expansion, command substitution, arithmetic substitution, and + quote removal of WORD. Return a pointer to a WORD_LIST which is + the result of the expansion. If WORD contains a null word, the + word list returned is also null. + + QUOTED contains flag values defined in shell.h. + + ISEXP is used to tell expand_word_internal that the word should be + treated as the result of an expansion. This has implications for + how IFS characters in the word are treated. + + CONTAINS_DOLLAR_AT and EXPANDED_SOMETHING are return values; when non-null + they point to an integer value which receives information about expansion. + CONTAINS_DOLLAR_AT gets non-zero if WORD contained "$@", else zero. + EXPANDED_SOMETHING get non-zero if WORD contained any parameter expansions, + else zero. + + This only does word splitting in the case of $@ expansion. In that + case, we split on ' '. */ + +/* Values for the local variable quoted_state. */ +#define UNQUOTED 0 +#define PARTIALLY_QUOTED 1 +#define WHOLLY_QUOTED 2 + +static WORD_LIST * +expand_word_internal (word, quoted, isexp, contains_dollar_at, expanded_something) + WORD_DESC *word; + int quoted, isexp; + int *contains_dollar_at; + int *expanded_something; +{ + WORD_LIST *list; + WORD_DESC *tword; + + /* The intermediate string that we build while expanding. */ + char *istring; + + /* The current size of the above object. */ + int istring_size; + + /* Index into ISTRING. */ + int istring_index; + + /* Temporary string storage. */ + char *temp, *temp1; + + /* The text of WORD. */ + register char *string; + + /* The size of STRING. */ + size_t string_size; + + /* The index into STRING. */ + int sindex; + + /* This gets 1 if we see a $@ while quoted. */ + int quoted_dollar_at; + + /* One of UNQUOTED, PARTIALLY_QUOTED, or WHOLLY_QUOTED, depending on + whether WORD contains no quoting characters, a partially quoted + string (e.g., "xx"ab), or is fully quoted (e.g., "xxab"). */ + int quoted_state; + + /* State flags */ + int had_quoted_null; + int has_dollar_at; + int tflag; + int pflags; /* flags passed to param_expand */ + + int assignoff; /* If assignment, offset of `=' */ + + register unsigned char c; /* Current character. */ + int t_index; /* For calls to string_extract_xxx. */ + + char twochars[2]; + + DECLARE_MBSTATE; + + istring = (char *)xmalloc (istring_size = DEFAULT_INITIAL_ARRAY_SIZE); + istring[istring_index = 0] = '\0'; + quoted_dollar_at = had_quoted_null = has_dollar_at = 0; + quoted_state = UNQUOTED; + + string = word->word; + if (string == 0) + goto finished_with_string; + /* Don't need the string length for the SADD... and COPY_ macros unless + multibyte characters are possible. */ + string_size = (MB_CUR_MAX > 1) ? strlen (string) : 1; + + if (contains_dollar_at) + *contains_dollar_at = 0; + + assignoff = -1; + + /* Begin the expansion. */ + + for (sindex = 0; ;) + { + c = string[sindex]; + + /* Case on toplevel character. */ + switch (c) + { + case '\0': + goto finished_with_string; + + case CTLESC: + sindex++; +#if HANDLE_MULTIBYTE + if (MB_CUR_MAX > 1 && string[sindex]) + { + SADD_MBQCHAR_BODY(temp, string, sindex, string_size); + } + else +#endif + { + temp = (char *)xmalloc (3); + temp[0] = CTLESC; + temp[1] = c = string[sindex]; + temp[2] = '\0'; + } + +dollar_add_string: + if (string[sindex]) + sindex++; + +add_string: + if (temp) + { + istring = sub_append_string (temp, istring, &istring_index, &istring_size); + temp = (char *)0; + } + + break; + +#if defined (PROCESS_SUBSTITUTION) + /* Process substitution. */ + case '<': + case '>': + { + if (string[++sindex] != LPAREN || (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (word->flags & (W_DQUOTE|W_NOPROCSUB)) || posixly_correct) + { + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + else + t_index = sindex + 1; /* skip past both '<' and LPAREN */ + + temp1 = extract_process_subst (string, (c == '<') ? "<(" : ">(", &t_index); /*))*/ + sindex = t_index; + + /* If the process substitution specification is `<()', we want to + open the pipe for writing in the child and produce output; if + it is `>()', we want to open the pipe for reading in the child + and consume input. */ + temp = temp1 ? process_substitute (temp1, (c == '>')) : (char *)0; + + FREE (temp1); + + goto dollar_add_string; + } +#endif /* PROCESS_SUBSTITUTION */ + + case '=': + /* Posix.2 section 3.6.1 says that tildes following `=' in words + which are not assignment statements are not expanded. If the + shell isn't in posix mode, though, we perform tilde expansion + on `likely candidate' unquoted assignment statements (flags + include W_ASSIGNMENT but not W_QUOTED). A likely candidate + contains an unquoted :~ or =~. Something to think about: we + now have a flag that says to perform tilde expansion on arguments + to `assignment builtins' like declare and export that look like + assignment statements. We now do tilde expansion on such words + even in POSIX mode. */ + if (word->flags & (W_ASSIGNRHS|W_NOTILDE)) + { + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + } + /* If we're not in posix mode or forcing assignment-statement tilde + expansion, note where the `=' appears in the word and prepare to + do tilde expansion following the first `='. */ + if ((word->flags & W_ASSIGNMENT) && + (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && + assignoff == -1 && sindex > 0) + assignoff = sindex; + if (sindex == assignoff && string[sindex+1] == '~') /* XXX */ + word->flags |= W_ITILDE; +#if 0 + else if ((word->flags & W_ASSIGNMENT) && + (posixly_correct == 0 || (word->flags & W_TILDEEXP)) && + string[sindex+1] == '~') + word->flags |= W_ITILDE; +#endif + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + + case ':': + if (word->flags & W_NOTILDE) + { + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + } + + if ((word->flags & (W_ASSIGNMENT|W_ASSIGNRHS|W_TILDEEXP)) && + string[sindex+1] == '~') + word->flags |= W_ITILDE; + + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c)) + goto add_ifs_character; + else + goto add_character; + + case '~': + /* If the word isn't supposed to be tilde expanded, or we're not + at the start of a word or after an unquoted : or = in an + assignment statement, we don't do tilde expansion. */ + if ((word->flags & (W_NOTILDE|W_DQUOTE)) || + (sindex > 0 && ((word->flags & W_ITILDE) == 0)) || + (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + { + word->flags &= ~W_ITILDE; + if (isexp == 0 && (word->flags & (W_NOSPLIT|W_NOSPLIT2)) == 0 && isifs (c) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) == 0) + goto add_ifs_character; + else + goto add_character; + } + + if (word->flags & W_ASSIGNRHS) + tflag = 2; + else if (word->flags & (W_ASSIGNMENT|W_TILDEEXP)) + tflag = 1; + else + tflag = 0; + + temp = bash_tilde_find_word (string + sindex, tflag, &t_index); + + word->flags &= ~W_ITILDE; + + if (temp && *temp && t_index > 0) + { + temp1 = bash_tilde_expand (temp, tflag); + if (temp1 && *temp1 == '~' && STREQ (temp, temp1)) + { + FREE (temp); + FREE (temp1); + goto add_character; /* tilde expansion failed */ + } + free (temp); + temp = temp1; + sindex += t_index; + goto add_quoted_string; /* XXX was add_string */ + } + else + { + FREE (temp); + goto add_character; + } + + case '$': + if (expanded_something) + *expanded_something = 1; + + has_dollar_at = 0; + pflags = (word->flags & W_NOCOMSUB) ? PF_NOCOMSUB : 0; + if (word->flags & W_NOSPLIT2) + pflags |= PF_NOSPLIT2; + tword = param_expand (string, &sindex, quoted, expanded_something, + &has_dollar_at, "ed_dollar_at, + &had_quoted_null, pflags); + + if (tword == &expand_wdesc_error || tword == &expand_wdesc_fatal) + { + free (string); + free (istring); + return ((tword == &expand_wdesc_error) ? &expand_word_error + : &expand_word_fatal); + } + if (contains_dollar_at && has_dollar_at) + *contains_dollar_at = 1; + + if (tword && (tword->flags & W_HASQUOTEDNULL)) + had_quoted_null = 1; + + temp = tword->word; + dispose_word_desc (tword); + + goto add_string; + break; + + case '`': /* Backquoted command substitution. */ + { + t_index = sindex++; + + temp = string_extract (string, &sindex, "`", SX_REQMATCH); + /* The test of sindex against t_index is to allow bare instances of + ` to pass through, for backwards compatibility. */ + if (temp == &extract_string_error || temp == &extract_string_fatal) + { + if (sindex - 1 == t_index) + { + sindex = t_index; + goto add_character; + } + report_error (_("bad substitution: no closing \"`\" in %s") , string+t_index); + free (string); + free (istring); + return ((temp == &extract_string_error) ? &expand_word_error + : &expand_word_fatal); + } + + if (expanded_something) + *expanded_something = 1; + + if (word->flags & W_NOCOMSUB) + /* sindex + 1 because string[sindex] == '`' */ + temp1 = substring (string, t_index, sindex + 1); + else + { + de_backslash (temp); + tword = command_substitute (temp, quoted); + temp1 = tword ? tword->word : (char *)NULL; + if (tword) + dispose_word_desc (tword); + } + FREE (temp); + temp = temp1; + goto dollar_add_string; + } + + case '\\': + if (string[sindex + 1] == '\n') + { + sindex += 2; + continue; + } + + c = string[++sindex]; + + if (quoted & Q_HERE_DOCUMENT) + tflag = CBSHDOC; + else if (quoted & Q_DOUBLE_QUOTES) + tflag = CBSDQUOTE; + else + tflag = 0; + + /* From Posix discussion on austin-group list: Backslash escaping + a } in ${...} is removed. Issue 0000221 */ + if ((quoted & Q_DOLBRACE) && c == RBRACE) + { + SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size); + } + else if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && ((sh_syntaxtab[c] & tflag) == 0)) + { + SCOPY_CHAR_I (twochars, '\\', c, string, sindex, string_size); + } + else if (c == 0) + { + c = CTLNUL; + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + else + { + SCOPY_CHAR_I (twochars, CTLESC, c, string, sindex, string_size); + } + + sindex++; +add_twochars: + /* BEFORE jumping here, we need to increment sindex if appropriate */ + RESIZE_MALLOCED_BUFFER (istring, istring_index, 2, istring_size, + DEFAULT_ARRAY_SIZE); + istring[istring_index++] = twochars[0]; + istring[istring_index++] = twochars[1]; + istring[istring_index] = '\0'; + + break; + + case '"': +#if 0 + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (word->flags & W_DQUOTE)) +#else + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) +#endif + goto add_character; + + t_index = ++sindex; + temp = string_extract_double_quoted (string, &sindex, 0); + + /* If the quotes surrounded the entire string, then the + whole word was quoted. */ + quoted_state = (t_index == 1 && string[sindex] == '\0') + ? WHOLLY_QUOTED + : PARTIALLY_QUOTED; + + if (temp && *temp) + { + tword = alloc_word_desc (); + tword->word = temp; + + temp = (char *)NULL; + + has_dollar_at = 0; + /* Need to get W_HASQUOTEDNULL flag through this function. */ + list = expand_word_internal (tword, Q_DOUBLE_QUOTES, 0, &has_dollar_at, (int *)NULL); + + if (list == &expand_word_error || list == &expand_word_fatal) + { + free (istring); + free (string); + /* expand_word_internal has already freed temp_word->word + for us because of the way it prints error messages. */ + tword->word = (char *)NULL; + dispose_word (tword); + return list; + } + + dispose_word (tword); + + /* "$@" (a double-quoted dollar-at) expands into nothing, + not even a NULL word, when there are no positional + parameters. */ + if (list == 0 && has_dollar_at) + { + quoted_dollar_at++; + break; + } + + /* If we get "$@", we know we have expanded something, so we + need to remember it for the final split on $IFS. This is + a special case; it's the only case where a quoted string + can expand into more than one word. It's going to come back + from the above call to expand_word_internal as a list with + a single word, in which all characters are quoted and + separated by blanks. What we want to do is to turn it back + into a list for the next piece of code. */ + if (list) + dequote_list (list); + + if (list && list->word && (list->word->flags & W_HASQUOTEDNULL)) + had_quoted_null = 1; + + if (has_dollar_at) + { + quoted_dollar_at++; + if (contains_dollar_at) + *contains_dollar_at = 1; + if (expanded_something) + *expanded_something = 1; + } + } + else + { + /* What we have is "". This is a minor optimization. */ + FREE (temp); + list = (WORD_LIST *)NULL; + } + + /* The code above *might* return a list (consider the case of "$@", + where it returns "$1", "$2", etc.). We can't throw away the + rest of the list, and we have to make sure each word gets added + as quoted. We test on tresult->next: if it is non-NULL, we + quote the whole list, save it to a string with string_list, and + add that string. We don't need to quote the results of this + (and it would be wrong, since that would quote the separators + as well), so we go directly to add_string. */ + if (list) + { + if (list->next) + { +#if 0 + if (quoted_dollar_at && word->flags & W_NOSPLIT2) + temp = string_list_internal (quote_list (list), " "); + else +#endif + /* Testing quoted_dollar_at makes sure that "$@" is + split correctly when $IFS does not contain a space. */ + temp = quoted_dollar_at + ? string_list_dollar_at (list, Q_DOUBLE_QUOTES) + : string_list (quote_list (list)); + dispose_words (list); + goto add_string; + } + else + { + temp = savestring (list->word->word); + tflag = list->word->flags; + dispose_words (list); + + /* If the string is not a quoted null string, we want + to remove any embedded unquoted CTLNUL characters. + We do not want to turn quoted null strings back into + the empty string, though. We do this because we + want to remove any quoted nulls from expansions that + contain other characters. For example, if we have + x"$*"y or "x$*y" and there are no positional parameters, + the $* should expand into nothing. */ + /* We use the W_HASQUOTEDNULL flag to differentiate the + cases: a quoted null character as above and when + CTLNUL is contained in the (non-null) expansion + of some variable. We use the had_quoted_null flag to + pass the value through this function to its caller. */ + if ((tflag & W_HASQUOTEDNULL) && QUOTED_NULL (temp) == 0) + remove_quoted_nulls (temp); /* XXX */ + } + } + else + temp = (char *)NULL; + +#if 0 + /* We do not want to add quoted nulls to strings that are only + partially quoted; we can throw them away. */ + if (temp == 0 && quoted_state == PARTIALLY_QUOTED) + continue; +#endif + + add_quoted_string: + + if (temp) + { + temp1 = temp; + temp = quote_string (temp); + free (temp1); + goto add_string; + } + else + { + /* Add NULL arg. */ + c = CTLNUL; + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + + /* break; */ + + case '\'': +#if 0 + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (word->flags & W_DQUOTE)) +#else + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) +#endif + goto add_character; + + t_index = ++sindex; + temp = string_extract_single_quoted (string, &sindex); + + /* If the entire STRING was surrounded by single quotes, + then the string is wholly quoted. */ + quoted_state = (t_index == 1 && string[sindex] == '\0') + ? WHOLLY_QUOTED + : PARTIALLY_QUOTED; + + /* If all we had was '', it is a null expansion. */ + if (*temp == '\0') + { + free (temp); + temp = (char *)NULL; + } + else + remove_quoted_escapes (temp); /* ??? */ + + /* We do not want to add quoted nulls to strings that are only + partially quoted; such nulls are discarded. */ + if (temp == 0 && (quoted_state == PARTIALLY_QUOTED)) + continue; + + /* If we have a quoted null expansion, add a quoted NULL to istring. */ + if (temp == 0) + { + c = CTLNUL; + sindex--; /* add_character: label increments sindex */ + goto add_character; + } + else + goto add_quoted_string; + + /* break; */ + + default: + /* This is the fix for " $@ " */ + add_ifs_character: + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || (isexp == 0 && isifs (c))) + { + if (string[sindex]) /* from old goto dollar_add_string */ + sindex++; + if (c == 0) + { + c = CTLNUL; + goto add_character; + } + else + { +#if HANDLE_MULTIBYTE + if (MB_CUR_MAX > 1) + sindex--; + + if (MB_CUR_MAX > 1) + { + SADD_MBQCHAR_BODY(temp, string, sindex, string_size); + } + else +#endif + { + twochars[0] = CTLESC; + twochars[1] = c; + goto add_twochars; + } + } + } + + SADD_MBCHAR (temp, string, sindex, string_size); + + add_character: + RESIZE_MALLOCED_BUFFER (istring, istring_index, 1, istring_size, + DEFAULT_ARRAY_SIZE); + istring[istring_index++] = c; + istring[istring_index] = '\0'; + + /* Next character. */ + sindex++; + } + } + +finished_with_string: + /* OK, we're ready to return. If we have a quoted string, and + quoted_dollar_at is not set, we do no splitting at all; otherwise + we split on ' '. The routines that call this will handle what to + do if nothing has been expanded. */ + + /* Partially and wholly quoted strings which expand to the empty + string are retained as an empty arguments. Unquoted strings + which expand to the empty string are discarded. The single + exception is the case of expanding "$@" when there are no + positional parameters. In that case, we discard the expansion. */ + + /* Because of how the code that handles "" and '' in partially + quoted strings works, we need to make ISTRING into a QUOTED_NULL + if we saw quoting characters, but the expansion was empty. + "" and '' are tossed away before we get to this point when + processing partially quoted strings. This makes "" and $xxx"" + equivalent when xxx is unset. We also look to see whether we + saw a quoted null from a ${} expansion and add one back if we + need to. */ + + /* If we expand to nothing and there were no single or double quotes + in the word, we throw it away. Otherwise, we return a NULL word. + The single exception is for $@ surrounded by double quotes when + there are no positional parameters. In that case, we also throw + the word away. */ + + if (*istring == '\0') + { + if (quoted_dollar_at == 0 && (had_quoted_null || quoted_state == PARTIALLY_QUOTED)) + { + istring[0] = CTLNUL; + istring[1] = '\0'; + tword = make_bare_word (istring); + tword->flags |= W_HASQUOTEDNULL; /* XXX */ + list = make_word_list (tword, (WORD_LIST *)NULL); + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + tword->flags |= W_QUOTED; + } + /* According to sh, ksh, and Posix.2, if a word expands into nothing + and a double-quoted "$@" appears anywhere in it, then the entire + word is removed. */ + else if (quoted_state == UNQUOTED || quoted_dollar_at) + list = (WORD_LIST *)NULL; +#if 0 + else + { + tword = make_bare_word (istring); + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + tword->flags |= W_QUOTED; + list = make_word_list (tword, (WORD_LIST *)NULL); + } +#else + else + list = (WORD_LIST *)NULL; +#endif + } + else if (word->flags & W_NOSPLIT) + { + tword = make_bare_word (istring); + if (word->flags & W_ASSIGNMENT) + tword->flags |= W_ASSIGNMENT; /* XXX */ + if (word->flags & W_COMPASSIGN) + tword->flags |= W_COMPASSIGN; /* XXX */ + if (word->flags & W_NOGLOB) + tword->flags |= W_NOGLOB; /* XXX */ + if (word->flags & W_NOEXPAND) + tword->flags |= W_NOEXPAND; /* XXX */ + if (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) + tword->flags |= W_QUOTED; + if (had_quoted_null) + tword->flags |= W_HASQUOTEDNULL; + list = make_word_list (tword, (WORD_LIST *)NULL); + } + else + { + char *ifs_chars; + + ifs_chars = (quoted_dollar_at || has_dollar_at) ? ifs_value : (char *)NULL; + + /* If we have $@, we need to split the results no matter what. If + IFS is unset or NULL, string_list_dollar_at has separated the + positional parameters with a space, so we split on space (we have + set ifs_chars to " \t\n" above if ifs is unset). If IFS is set, + string_list_dollar_at has separated the positional parameters + with the first character of $IFS, so we split on $IFS. */ + if (has_dollar_at && ifs_chars) + list = list_string (istring, *ifs_chars ? ifs_chars : " ", 1); + else + { + tword = make_bare_word (istring); + if ((quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) || (quoted_state == WHOLLY_QUOTED)) + tword->flags |= W_QUOTED; + if (word->flags & W_ASSIGNMENT) + tword->flags |= W_ASSIGNMENT; + if (word->flags & W_COMPASSIGN) + tword->flags |= W_COMPASSIGN; + if (word->flags & W_NOGLOB) + tword->flags |= W_NOGLOB; + if (word->flags & W_NOEXPAND) + tword->flags |= W_NOEXPAND; + if (had_quoted_null) + tword->flags |= W_HASQUOTEDNULL; /* XXX */ + list = make_word_list (tword, (WORD_LIST *)NULL); + } + } + + free (istring); + return (list); +} + +/* **************************************************************** */ +/* */ +/* Functions for Quote Removal */ +/* */ +/* **************************************************************** */ + +/* Perform quote removal on STRING. If QUOTED > 0, assume we are obeying the + backslash quoting rules for within double quotes or a here document. */ +char * +string_quote_removal (string, quoted) + char *string; + int quoted; +{ + size_t slen; + char *r, *result_string, *temp, *send; + int sindex, tindex, dquote; + unsigned char c; + DECLARE_MBSTATE; + + /* The result can be no longer than the original string. */ + slen = strlen (string); + send = string + slen; + + r = result_string = (char *)xmalloc (slen + 1); + + for (dquote = sindex = 0; c = string[sindex];) + { + switch (c) + { + case '\\': + c = string[++sindex]; + if (c == 0) + { + *r++ = '\\'; + break; + } + if (((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) && (sh_syntaxtab[c] & CBSDQUOTE) == 0) + *r++ = '\\'; + /* FALLTHROUGH */ + + default: + SCOPY_CHAR_M (r, string, send, sindex); + break; + + case '\'': + if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) || dquote) + { + *r++ = c; + sindex++; + break; + } + tindex = sindex + 1; + temp = string_extract_single_quoted (string, &tindex); + if (temp) + { + strcpy (r, temp); + r += strlen (r); + free (temp); + } + sindex = tindex; + break; + + case '"': + dquote = 1 - dquote; + sindex++; + break; + } + } + *r = '\0'; + return (result_string); +} + +#if 0 +/* UNUSED */ +/* Perform quote removal on word WORD. This allocates and returns a new + WORD_DESC *. */ +WORD_DESC * +word_quote_removal (word, quoted) + WORD_DESC *word; + int quoted; +{ + WORD_DESC *w; + char *t; + + t = string_quote_removal (word->word, quoted); + w = alloc_word_desc (); + w->word = t ? t : savestring (""); + return (w); +} + +/* Perform quote removal on all words in LIST. If QUOTED is non-zero, + the members of the list are treated as if they are surrounded by + double quotes. Return a new list, or NULL if LIST is NULL. */ +WORD_LIST * +word_list_quote_removal (list, quoted) + WORD_LIST *list; + int quoted; +{ + WORD_LIST *result, *t, *tresult, *e; + + for (t = list, result = (WORD_LIST *)NULL; t; t = t->next) + { + tresult = make_word_list (word_quote_removal (t->word, quoted), (WORD_LIST *)NULL); +#if 0 + result = (WORD_LIST *) list_append (result, tresult); +#else + if (result == 0) + result = e = tresult; + else + { + e->next = tresult; + while (e->next) + e = e->next; + } +#endif + } + return (result); +} +#endif + +/******************************************* + * * + * Functions to perform word splitting * + * * + *******************************************/ + +void +setifs (v) + SHELL_VAR *v; +{ + char *t; + unsigned char uc; + + ifs_var = v; + ifs_value = (v && value_cell (v)) ? value_cell (v) : " \t\n"; + + /* Should really merge ifs_cmap with sh_syntaxtab. XXX - doesn't yet + handle multibyte chars in IFS */ + memset (ifs_cmap, '\0', sizeof (ifs_cmap)); + for (t = ifs_value ; t && *t; t++) + { + uc = *t; + ifs_cmap[uc] = 1; + } + +#if defined (HANDLE_MULTIBYTE) + if (ifs_value == 0) + { + ifs_firstc[0] = '\0'; + ifs_firstc_len = 1; + } + else + { + size_t ifs_len; + ifs_len = strnlen (ifs_value, MB_CUR_MAX); + ifs_firstc_len = MBLEN (ifs_value, ifs_len); + if (ifs_firstc_len == 1 || ifs_firstc_len == 0 || MB_INVALIDCH (ifs_firstc_len)) + { + ifs_firstc[0] = ifs_value[0]; + ifs_firstc[1] = '\0'; + ifs_firstc_len = 1; + } + else + memcpy (ifs_firstc, ifs_value, ifs_firstc_len); + } +#else + ifs_firstc = ifs_value ? *ifs_value : 0; +#endif +} + +char * +getifs () +{ + return ifs_value; +} + +/* This splits a single word into a WORD LIST on $IFS, but only if the word + is not quoted. list_string () performs quote removal for us, even if we + don't do any splitting. */ +WORD_LIST * +word_split (w, ifs_chars) + WORD_DESC *w; + char *ifs_chars; +{ + WORD_LIST *result; + + if (w) + { + char *xifs; + + xifs = ((w->flags & W_QUOTED) || ifs_chars == 0) ? "" : ifs_chars; + result = list_string (w->word, xifs, w->flags & W_QUOTED); + } + else + result = (WORD_LIST *)NULL; + + return (result); +} + +/* Perform word splitting on LIST and return the RESULT. It is possible + to return (WORD_LIST *)NULL. */ +static WORD_LIST * +word_list_split (list) + WORD_LIST *list; +{ + WORD_LIST *result, *t, *tresult, *e; + + for (t = list, result = (WORD_LIST *)NULL; t; t = t->next) + { + tresult = word_split (t->word, ifs_value); + if (result == 0) + result = e = tresult; + else + { + e->next = tresult; + while (e->next) + e = e->next; + } + } + return (result); +} + +/************************************************** + * * + * Functions to expand an entire WORD_LIST * + * * + **************************************************/ + +/* Do any word-expansion-specific cleanup and jump to top_level */ +static void +exp_jump_to_top_level (v) + int v; +{ + set_pipestatus_from_exit (last_command_exit_value); + + /* Cleanup code goes here. */ + expand_no_split_dollar_star = 0; /* XXX */ + expanding_redir = 0; + assigning_in_environment = 0; + + if (parse_and_execute_level == 0) + top_level_cleanup (); /* from sig.c */ + + jump_to_top_level (v); +} + +/* Put NLIST (which is a WORD_LIST * of only one element) at the front of + ELIST, and set ELIST to the new list. */ +#define PREPEND_LIST(nlist, elist) \ + do { nlist->next = elist; elist = nlist; } while (0) + +/* Separate out any initial variable assignments from TLIST. If set -k has + been executed, remove all assignment statements from TLIST. Initial + variable assignments and other environment assignments are placed + on SUBST_ASSIGN_VARLIST. */ +static WORD_LIST * +separate_out_assignments (tlist) + WORD_LIST *tlist; +{ + register WORD_LIST *vp, *lp; + + if (tlist == 0) + return ((WORD_LIST *)NULL); + + if (subst_assign_varlist) + dispose_words (subst_assign_varlist); /* Clean up after previous error */ + + subst_assign_varlist = (WORD_LIST *)NULL; + vp = lp = tlist; + + /* Separate out variable assignments at the start of the command. + Loop invariant: vp->next == lp + Loop postcondition: + lp = list of words left after assignment statements skipped + tlist = original list of words + */ + while (lp && (lp->word->flags & W_ASSIGNMENT)) + { + vp = lp; + lp = lp->next; + } + + /* If lp != tlist, we have some initial assignment statements. + We make SUBST_ASSIGN_VARLIST point to the list of assignment + words and TLIST point to the remaining words. */ + if (lp != tlist) + { + subst_assign_varlist = tlist; + /* ASSERT(vp->next == lp); */ + vp->next = (WORD_LIST *)NULL; /* terminate variable list */ + tlist = lp; /* remainder of word list */ + } + + /* vp == end of variable list */ + /* tlist == remainder of original word list without variable assignments */ + if (!tlist) + /* All the words in tlist were assignment statements */ + return ((WORD_LIST *)NULL); + + /* ASSERT(tlist != NULL); */ + /* ASSERT((tlist->word->flags & W_ASSIGNMENT) == 0); */ + + /* If the -k option is in effect, we need to go through the remaining + words, separate out the assignment words, and place them on + SUBST_ASSIGN_VARLIST. */ + if (place_keywords_in_env) + { + WORD_LIST *tp; /* tp == running pointer into tlist */ + + tp = tlist; + lp = tlist->next; + + /* Loop Invariant: tp->next == lp */ + /* Loop postcondition: tlist == word list without assignment statements */ + while (lp) + { + if (lp->word->flags & W_ASSIGNMENT) + { + /* Found an assignment statement, add this word to end of + subst_assign_varlist (vp). */ + if (!subst_assign_varlist) + subst_assign_varlist = vp = lp; + else + { + vp->next = lp; + vp = lp; + } + + /* Remove the word pointed to by LP from TLIST. */ + tp->next = lp->next; + /* ASSERT(vp == lp); */ + lp->next = (WORD_LIST *)NULL; + lp = tp->next; + } + else + { + tp = lp; + lp = lp->next; + } + } + } + return (tlist); +} + +#define WEXP_VARASSIGN 0x001 +#define WEXP_BRACEEXP 0x002 +#define WEXP_TILDEEXP 0x004 +#define WEXP_PARAMEXP 0x008 +#define WEXP_PATHEXP 0x010 + +/* All of the expansions, including variable assignments at the start of + the list. */ +#define WEXP_ALL (WEXP_VARASSIGN|WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP) + +/* All of the expansions except variable assignments at the start of + the list. */ +#define WEXP_NOVARS (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP|WEXP_PATHEXP) + +/* All of the `shell expansions': brace expansion, tilde expansion, parameter + expansion, command substitution, arithmetic expansion, word splitting, and + quote removal. */ +#define WEXP_SHELLEXP (WEXP_BRACEEXP|WEXP_TILDEEXP|WEXP_PARAMEXP) + +/* Take the list of words in LIST and do the various substitutions. Return + a new list of words which is the expanded list, and without things like + variable assignments. */ + +WORD_LIST * +expand_words (list) + WORD_LIST *list; +{ + return (expand_word_list_internal (list, WEXP_ALL)); +} + +/* Same as expand_words (), but doesn't hack variable or environment + variables. */ +WORD_LIST * +expand_words_no_vars (list) + WORD_LIST *list; +{ + return (expand_word_list_internal (list, WEXP_NOVARS)); +} + +WORD_LIST * +expand_words_shellexp (list) + WORD_LIST *list; +{ + return (expand_word_list_internal (list, WEXP_SHELLEXP)); +} + +static WORD_LIST * +glob_expand_word_list (tlist, eflags) + WORD_LIST *tlist; + int eflags; +{ + char **glob_array, *temp_string; + register int glob_index; + WORD_LIST *glob_list, *output_list, *disposables, *next; + WORD_DESC *tword; + + output_list = disposables = (WORD_LIST *)NULL; + glob_array = (char **)NULL; + while (tlist) + { + /* For each word, either globbing is attempted or the word is + added to orig_list. If globbing succeeds, the results are + added to orig_list and the word (tlist) is added to the list + of disposable words. If globbing fails and failed glob + expansions are left unchanged (the shell default), the + original word is added to orig_list. If globbing fails and + failed glob expansions are removed, the original word is + added to the list of disposable words. orig_list ends up + in reverse order and requires a call to REVERSE_LIST to + be set right. After all words are examined, the disposable + words are freed. */ + next = tlist->next; + + /* If the word isn't an assignment and contains an unquoted + pattern matching character, then glob it. */ + if ((tlist->word->flags & W_NOGLOB) == 0 && + unquoted_glob_pattern_p (tlist->word->word)) + { + glob_array = shell_glob_filename (tlist->word->word); + + /* Handle error cases. + I don't think we should report errors like "No such file + or directory". However, I would like to report errors + like "Read failed". */ + + if (glob_array == 0 || GLOB_FAILED (glob_array)) + { + glob_array = (char **)xmalloc (sizeof (char *)); + glob_array[0] = (char *)NULL; + } + + /* Dequote the current word in case we have to use it. */ + if (glob_array[0] == NULL) + { + temp_string = dequote_string (tlist->word->word); + free (tlist->word->word); + tlist->word->word = temp_string; + } + + /* Make the array into a word list. */ + glob_list = (WORD_LIST *)NULL; + for (glob_index = 0; glob_array[glob_index]; glob_index++) + { + tword = make_bare_word (glob_array[glob_index]); + tword->flags |= W_GLOBEXP; /* XXX */ + glob_list = make_word_list (tword, glob_list); + } + + if (glob_list) + { + output_list = (WORD_LIST *)list_append (glob_list, output_list); + PREPEND_LIST (tlist, disposables); + } + else if (fail_glob_expansion != 0) + { + report_error (_("no match: %s"), tlist->word->word); + exp_jump_to_top_level (DISCARD); + } + else if (allow_null_glob_expansion == 0) + { + /* Failed glob expressions are left unchanged. */ + PREPEND_LIST (tlist, output_list); + } + else + { + /* Failed glob expressions are removed. */ + PREPEND_LIST (tlist, disposables); + } + } + else + { + /* Dequote the string. */ + temp_string = dequote_string (tlist->word->word); + free (tlist->word->word); + tlist->word->word = temp_string; + PREPEND_LIST (tlist, output_list); + } + + strvec_dispose (glob_array); + glob_array = (char **)NULL; + + tlist = next; + } + + if (disposables) + dispose_words (disposables); + + if (output_list) + output_list = REVERSE_LIST (output_list, WORD_LIST *); + + return (output_list); +} + +#if defined (BRACE_EXPANSION) +static WORD_LIST * +brace_expand_word_list (tlist, eflags) + WORD_LIST *tlist; + int eflags; +{ + register char **expansions; + char *temp_string; + WORD_LIST *disposables, *output_list, *next; + WORD_DESC *w; + int eindex; + + for (disposables = output_list = (WORD_LIST *)NULL; tlist; tlist = next) + { + next = tlist->next; + + if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG)) + { +/*itrace("brace_expand_word_list: %s: W_COMPASSIGN|W_ASSIGNARG", tlist->word->word);*/ + PREPEND_LIST (tlist, output_list); + continue; + } + + /* Only do brace expansion if the word has a brace character. If + not, just add the word list element to BRACES and continue. In + the common case, at least when running shell scripts, this will + degenerate to a bunch of calls to `mbschr', and then what is + basically a reversal of TLIST into BRACES, which is corrected + by a call to REVERSE_LIST () on BRACES when the end of TLIST + is reached. */ + if (mbschr (tlist->word->word, LBRACE)) + { + expansions = brace_expand (tlist->word->word); + + for (eindex = 0; temp_string = expansions[eindex]; eindex++) + { + w = make_word (temp_string); + /* If brace expansion didn't change the word, preserve + the flags. We may want to preserve the flags + unconditionally someday -- XXX */ + if (STREQ (temp_string, tlist->word->word)) + w->flags = tlist->word->flags; + output_list = make_word_list (w, output_list); + free (expansions[eindex]); + } + free (expansions); + + /* Add TLIST to the list of words to be freed after brace + expansion has been performed. */ + PREPEND_LIST (tlist, disposables); + } + else + PREPEND_LIST (tlist, output_list); + } + + if (disposables) + dispose_words (disposables); + + if (output_list) + output_list = REVERSE_LIST (output_list, WORD_LIST *); + + return (output_list); +} +#endif + +#if defined (ARRAY_VARS) +/* Take WORD, a compound associative array assignment, and internally run + 'declare -A w', where W is the variable name portion of WORD. */ +static int +make_internal_declare (word, option) + char *word; + char *option; +{ + int t; + WORD_LIST *wl; + WORD_DESC *w; + + w = make_word (word); + + t = assignment (w->word, 0); + w->word[t] = '\0'; + + wl = make_word_list (w, (WORD_LIST *)NULL); + wl = make_word_list (make_word (option), wl); + + return (declare_builtin (wl)); +} +#endif + +static WORD_LIST * +shell_expand_word_list (tlist, eflags) + WORD_LIST *tlist; + int eflags; +{ + WORD_LIST *expanded, *orig_list, *new_list, *next, *temp_list; + int expanded_something, has_dollar_at; + char *temp_string; + + /* We do tilde expansion all the time. This is what 1003.2 says. */ + new_list = (WORD_LIST *)NULL; + for (orig_list = tlist; tlist; tlist = next) + { + temp_string = tlist->word->word; + + next = tlist->next; + +#if defined (ARRAY_VARS) + /* If this is a compound array assignment to a builtin that accepts + such assignments (e.g., `declare'), take the assignment and perform + it separately, handling the semantics of declarations inside shell + functions. This avoids the double-evaluation of such arguments, + because `declare' does some evaluation of compound assignments on + its own. */ + if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG)) + { + int t; + + if (tlist->word->flags & W_ASSIGNASSOC) + make_internal_declare (tlist->word->word, "-A"); + + t = do_word_assignment (tlist->word); + if (t == 0) + { + last_command_exit_value = EXECUTION_FAILURE; + exp_jump_to_top_level (DISCARD); + } + + /* Now transform the word as ksh93 appears to do and go on */ + t = assignment (tlist->word->word, 0); + tlist->word->word[t] = '\0'; + tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC); + } +#endif + + expanded_something = 0; + expanded = expand_word_internal + (tlist->word, 0, 0, &has_dollar_at, &expanded_something); + + if (expanded == &expand_word_error || expanded == &expand_word_fatal) + { + /* By convention, each time this error is returned, + tlist->word->word has already been freed. */ + tlist->word->word = (char *)NULL; + + /* Dispose our copy of the original list. */ + dispose_words (orig_list); + /* Dispose the new list we're building. */ + dispose_words (new_list); + + last_command_exit_value = EXECUTION_FAILURE; + if (expanded == &expand_word_error) + exp_jump_to_top_level (DISCARD); + else + exp_jump_to_top_level (FORCE_EOF); + } + + /* Don't split words marked W_NOSPLIT. */ + if (expanded_something && (tlist->word->flags & W_NOSPLIT) == 0) + { + temp_list = word_list_split (expanded); + dispose_words (expanded); + } + else + { + /* If no parameter expansion, command substitution, process + substitution, or arithmetic substitution took place, then + do not do word splitting. We still have to remove quoted + null characters from the result. */ + word_list_remove_quoted_nulls (expanded); + temp_list = expanded; + } + + expanded = REVERSE_LIST (temp_list, WORD_LIST *); + new_list = (WORD_LIST *)list_append (expanded, new_list); + } + + if (orig_list) + dispose_words (orig_list); + + if (new_list) + new_list = REVERSE_LIST (new_list, WORD_LIST *); + + return (new_list); +} + +/* The workhorse for expand_words () and expand_words_no_vars (). + First arg is LIST, a WORD_LIST of words. + Second arg EFLAGS is a flags word controlling which expansions are + performed. + + This does all of the substitutions: brace expansion, tilde expansion, + parameter expansion, command substitution, arithmetic expansion, + process substitution, word splitting, and pathname expansion, according + to the bits set in EFLAGS. Words with the W_QUOTED or W_NOSPLIT bits + set, or for which no expansion is done, do not undergo word splitting. + Words with the W_NOGLOB bit set do not undergo pathname expansion. */ +static WORD_LIST * +expand_word_list_internal (list, eflags) + WORD_LIST *list; + int eflags; +{ + WORD_LIST *new_list, *temp_list; + int tint; + + if (list == 0) + return ((WORD_LIST *)NULL); + + garglist = new_list = copy_word_list (list); + if (eflags & WEXP_VARASSIGN) + { + garglist = new_list = separate_out_assignments (new_list); + if (new_list == 0) + { + if (subst_assign_varlist) + { + /* All the words were variable assignments, so they are placed + into the shell's environment. */ + for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next) + { + this_command_name = (char *)NULL; /* no arithmetic errors */ + tint = do_word_assignment (temp_list->word); + /* Variable assignment errors in non-interactive shells + running in Posix.2 mode cause the shell to exit. */ + if (tint == 0) + { + last_command_exit_value = EXECUTION_FAILURE; + if (interactive_shell == 0 && posixly_correct) + exp_jump_to_top_level (FORCE_EOF); + else + exp_jump_to_top_level (DISCARD); + } + } + dispose_words (subst_assign_varlist); + subst_assign_varlist = (WORD_LIST *)NULL; + } + return ((WORD_LIST *)NULL); + } + } + + /* Begin expanding the words that remain. The expansions take place on + things that aren't really variable assignments. */ + +#if defined (BRACE_EXPANSION) + /* Do brace expansion on this word if there are any brace characters + in the string. */ + if ((eflags & WEXP_BRACEEXP) && brace_expansion && new_list) + new_list = brace_expand_word_list (new_list, eflags); +#endif /* BRACE_EXPANSION */ + + /* Perform the `normal' shell expansions: tilde expansion, parameter and + variable substitution, command substitution, arithmetic expansion, + and word splitting. */ + new_list = shell_expand_word_list (new_list, eflags); + + /* Okay, we're almost done. Now let's just do some filename + globbing. */ + if (new_list) + { + if ((eflags & WEXP_PATHEXP) && disallow_filename_globbing == 0) + /* Glob expand the word list unless globbing has been disabled. */ + new_list = glob_expand_word_list (new_list, eflags); + else + /* Dequote the words, because we're not performing globbing. */ + new_list = dequote_list (new_list); + } + + if ((eflags & WEXP_VARASSIGN) && subst_assign_varlist) + { + sh_wassign_func_t *assign_func; + + /* If the remainder of the words expand to nothing, Posix.2 requires + that the variable and environment assignments affect the shell's + environment. */ + assign_func = new_list ? assign_in_env : do_word_assignment; + tempenv_assign_error = 0; + + for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next) + { + this_command_name = (char *)NULL; + assigning_in_environment = (assign_func == assign_in_env); + tint = (*assign_func) (temp_list->word); + assigning_in_environment = 0; + /* Variable assignment errors in non-interactive shells running + in Posix.2 mode cause the shell to exit. */ + if (tint == 0) + { + if (assign_func == do_word_assignment) + { + last_command_exit_value = EXECUTION_FAILURE; + if (interactive_shell == 0 && posixly_correct) + exp_jump_to_top_level (FORCE_EOF); + else + exp_jump_to_top_level (DISCARD); + } + else + tempenv_assign_error++; + } + } + + dispose_words (subst_assign_varlist); + subst_assign_varlist = (WORD_LIST *)NULL; + } + +#if 0 + tint = list_length (new_list) + 1; + RESIZE_MALLOCED_BUFFER (glob_argv_flags, 0, tint, glob_argv_flags_size, 16); + for (tint = 0, temp_list = new_list; temp_list; temp_list = temp_list->next) + glob_argv_flags[tint++] = (temp_list->word->flags & W_GLOBEXP) ? '1' : '0'; + glob_argv_flags[tint] = '\0'; +#endif + + return (new_list); +} @@ -245,10 +245,8 @@ static wchar_t *remove_wpattern __P((wchar_t *, size_t, wchar_t *, int)); #endif static char *remove_pattern __P((char *, char *, int)); -static int match_pattern_char __P((char *, char *)); static int match_upattern __P((char *, char *, int, char **, char **)); #if defined (HANDLE_MULTIBYTE) -static int match_pattern_wchar __P((wchar_t *, wchar_t *)); static int match_wpattern __P((wchar_t *, char **, size_t, wchar_t *, int, char **, char **)); #endif static int match_pattern __P((char *, char *, int, char **, char **)); @@ -260,7 +258,7 @@ static char *parameter_list_remove_pattern __P((int, char *, int, int)); #ifdef ARRAY_VARS static char *array_remove_pattern __P((SHELL_VAR *, char *, int, char *, int)); #endif -static char *parameter_brace_remove_pattern __P((char *, char *, char *, int, int)); +static char *parameter_brace_remove_pattern __P((char *, char *, int, char *, int, int, int)); static char *process_substitute __P((char *, int)); @@ -274,7 +272,7 @@ static int valid_brace_expansion_word __P((char *, int)); static int chk_atstar __P((char *, int, int *, int *)); static int chk_arithsub __P((const char *, int)); -static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int)); +static WORD_DESC *parameter_brace_expand_word __P((char *, int, int, int, arrayind_t *)); static WORD_DESC *parameter_brace_expand_indir __P((char *, int, int, int *, int *)); static WORD_DESC *parameter_brace_expand_rhs __P((char *, char *, int, int, int *, int *)); static void parameter_brace_expand_error __P((char *, char *)); @@ -284,18 +282,18 @@ static intmax_t parameter_brace_expand_length __P((char *)); static char *skiparith __P((char *, int)); static int verify_substring_values __P((SHELL_VAR *, char *, char *, int, intmax_t *, intmax_t *)); -static int get_var_and_type __P((char *, char *, int, SHELL_VAR **, char **)); +static int get_var_and_type __P((char *, char *, arrayind_t, int, int, SHELL_VAR **, char **)); static char *mb_substring __P((char *, int, int)); -static char *parameter_brace_substring __P((char *, char *, char *, int)); +static char *parameter_brace_substring __P((char *, char *, int, char *, int, int)); static int shouldexp_replacement __P((char *)); static char *pos_params_pat_subst __P((char *, char *, char *, int)); -static char *parameter_brace_patsub __P((char *, char *, char *, int)); +static char *parameter_brace_patsub __P((char *, char *, int, char *, int, int)); static char *pos_params_casemod __P((char *, char *, int, int)); -static char *parameter_brace_casemod __P((char *, char *, int, char *, int)); +static char *parameter_brace_casemod __P((char *, char *, int, int, char *, int, int)); static WORD_DESC *parameter_brace_expand __P((char *, int *, int, int, int *, int *)); static WORD_DESC *param_expand __P((char *, int *, int, int *, int *, int *, int *, int)); @@ -611,7 +609,6 @@ unquoted_substring (substr, string) { case '\\': sindex++; - if (string[sindex]) ADVANCE_CHAR (string, slen, sindex); break; @@ -3972,36 +3969,6 @@ remove_pattern (param, pattern, op) } } -/* Return 1 of the first character of STRING could match the first - character of pattern PAT. Used to avoid n2 calls to strmatch(). */ -static int -match_pattern_char (pat, string) - char *pat, *string; -{ - char c; - - if (*string == 0) - return (0); - - switch (c = *pat++) - { - default: - return (*string == c); - case '\\': - return (*string == *pat); - case '?': - return (*pat == LPAREN ? 1 : (*string != '\0')); - case '*': - return (1); - case '+': - case '!': - case '@': - return (*pat == LPAREN ? 1 : (*string == c)); - case '[': - return (*string != '\0'); - } -} - /* Match PAT anywhere in STRING and return the match boundaries. This returns 1 in case of a successful match, 0 otherwise. SP and EP are pointers into the string where the match begins and @@ -4014,7 +3981,7 @@ match_upattern (string, pat, mtype, sp, ep) int mtype; char **sp, **ep; { - int c, len; + int c, len, mlen; register char *p, *p1, *npat; char *end; @@ -4050,6 +4017,8 @@ match_upattern (string, pat, mtype, sp, ep) len = STRLEN (string); end = string + len; + mlen = umatchlen (pat, len); + switch (mtype) { case MATCH_ANY: @@ -4057,7 +4026,15 @@ match_upattern (string, pat, mtype, sp, ep) { if (match_pattern_char (pat, p)) { +#if 0 for (p1 = end; p1 >= p; p1--) +#else + p1 = (mlen == -1) ? end : p + mlen; + /* extra -1 to handle case of p1 == end */ + if (p1 - p + mlen - 1 > len) + break; + for ( ; p1 >= p; p1--) +#endif { c = *p1; *p1 = '\0'; if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) @@ -4068,6 +4045,11 @@ match_upattern (string, pat, mtype, sp, ep) return 1; } *p1 = c; +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } } } @@ -4078,7 +4060,11 @@ match_upattern (string, pat, mtype, sp, ep) if (match_pattern_char (pat, string) == 0) return (0); +#if 0 for (p = end; p >= string; p--) +#else + for (p = (mlen == -1) ? end : string + mlen; p >= string; p--) +#endif { c = *p; *p = '\0'; if (strmatch (pat, string, FNMATCH_EXTFLAG) == 0) @@ -4089,12 +4075,21 @@ match_upattern (string, pat, mtype, sp, ep) return 1; } *p = c; +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } return (0); case MATCH_END: +#if 0 for (p = string; p <= end; p++) +#else + for (p = end - ((mlen == -1) ? len : mlen); p <= end; p++) +#endif { if (strmatch (pat, p, FNMATCH_EXTFLAG) == 0) { @@ -4102,7 +4097,11 @@ match_upattern (string, pat, mtype, sp, ep) *ep = end; return 1; } - +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } return (0); @@ -4112,36 +4111,6 @@ match_upattern (string, pat, mtype, sp, ep) } #if defined (HANDLE_MULTIBYTE) -/* Return 1 of the first character of WSTRING could match the first - character of pattern WPAT. Wide character version. */ -static int -match_pattern_wchar (wpat, wstring) - wchar_t *wpat, *wstring; -{ - wchar_t wc; - - if (*wstring == 0) - return (0); - - switch (wc = *wpat++) - { - default: - return (*wstring == wc); - case L'\\': - return (*wstring == *wpat); - case L'?': - return (*wpat == LPAREN ? 1 : (*wstring != L'\0')); - case L'*': - return (1); - case L'+': - case L'!': - case L'@': - return (*wpat == LPAREN ? 1 : (*wstring == wc)); - case L'[': - return (*wstring != L'\0'); - } -} - /* Match WPAT anywhere in WSTRING and return the match boundaries. This returns 1 in case of a successful match, 0 otherwise. Wide character version. */ @@ -4155,12 +4124,9 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) char **sp, **ep; { wchar_t wc, *wp, *nwpat, *wp1; - int len; -#if 0 - size_t n, n1; /* Apple's gcc seems to miscompile this badly */ -#else + size_t len; + int mlen; int n, n1; -#endif /* If the pattern doesn't match anywhere in the string, go ahead and short-circuit right away. A minor optimization, saves a bunch of @@ -4168,8 +4134,6 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) characters) if the match is unsuccessful. To preserve the semantics of the substring matches below, we make sure that the pattern has `*' as first and last character, making a new pattern if necessary. */ - /* XXX - check this later if I ever implement `**' with special meaning, - since this will potentially result in `**' at the beginning or end */ len = wcslen (wpat); if (wpat[0] != L'*' || (wpat[0] == L'*' && wpat[1] == WLPAREN && extended_glob) || wpat[len - 1] != L'*') { @@ -4191,6 +4155,8 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) if (len == FNM_NOMATCH) return (0); + mlen = wmatchlen (wpat, wstrlen); +/* itrace("wmatchlen (%ls) -> %d", wpat, mlen); */ switch (mtype) { case MATCH_ANY: @@ -4198,7 +4164,15 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) { if (match_pattern_wchar (wpat, wstring + n)) { +#if 0 for (n1 = wstrlen; n1 >= n; n1--) +#else + n1 = (mlen == -1) ? wstrlen : n + mlen; + if (n1 > wstrlen) + break; + + for ( ; n1 >= n; n1--) +#endif { wc = wstring[n1]; wstring[n1] = L'\0'; if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) @@ -4209,6 +4183,11 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) return 1; } wstring[n1] = wc; +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } } } @@ -4219,7 +4198,11 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) if (match_pattern_wchar (wpat, wstring) == 0) return (0); +#if 0 for (n = wstrlen; n >= 0; n--) +#else + for (n = (mlen == -1) ? wstrlen : mlen; n >= 0; n--) +#endif { wc = wstring[n]; wstring[n] = L'\0'; if (wcsmatch (wpat, wstring, FNMATCH_EXTFLAG) == 0) @@ -4230,12 +4213,21 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) return 1; } wstring[n] = wc; +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } return (0); case MATCH_END: +#if 0 for (n = 0; n <= wstrlen; n++) +#else + for (n = wstrlen - ((mlen == -1) ? wstrlen : mlen); n <= wstrlen; n++) +#endif { if (wcsmatch (wpat, wstring + n, FNMATCH_EXTFLAG) == 0) { @@ -4243,6 +4235,11 @@ match_wpattern (wstring, indices, wstrlen, wpat, mtype, sp, ep) *ep = indices[wstrlen]; return 1; } +#if 1 + /* If MLEN != -1, we have a fixed length pattern. */ + if (mlen != -1) + break; +#endif } return (0); @@ -4446,9 +4443,11 @@ array_remove_pattern (var, pattern, patspec, varname, quoted) #endif /* ARRAY_VARS */ static char * -parameter_brace_remove_pattern (varname, value, patstr, rtype, quoted) - char *varname, *value, *patstr; - int rtype, quoted; +parameter_brace_remove_pattern (varname, value, ind, patstr, rtype, quoted, flags) + char *varname, *value; + int ind; + char *patstr; + int rtype, quoted, flags; { int vtype, patspec, starsub; char *temp1, *val, *pattern; @@ -4459,7 +4458,7 @@ parameter_brace_remove_pattern (varname, value, patstr, rtype, quoted) this_command_name = varname; - vtype = get_var_and_type (varname, value, quoted, &v, &val); + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); if (vtype == -1) return ((char *)NULL); @@ -5571,20 +5570,25 @@ chk_atstar (name, quoted, quoted_dollar_atp, contains_dollar_at) the shell, e.g., "@", "$", "*", etc. QUOTED, if non-zero, means that NAME was found inside of a double-quoted expression. */ static WORD_DESC * -parameter_brace_expand_word (name, var_is_special, quoted, pflags) +parameter_brace_expand_word (name, var_is_special, quoted, pflags, indp) char *name; int var_is_special, quoted, pflags; + arrayind_t *indp; { WORD_DESC *ret; char *temp, *tt; intmax_t arg_index; SHELL_VAR *var; int atype, rflags; + arrayind_t ind; ret = 0; temp = 0; rflags = 0; + if (indp) + *indp = INTMAX_MIN; + /* Handle multiple digit arguments, as in ${11}. */ if (legal_number (name, &arg_index)) { @@ -5611,11 +5615,16 @@ parameter_brace_expand_word (name, var_is_special, quoted, pflags) #if defined (ARRAY_VARS) else if (valid_array_reference (name)) { - temp = array_value (name, quoted, &atype, (arrayind_t *)NULL); + temp = array_value (name, quoted, 0, &atype, &ind); if (atype == 0 && temp) - temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) - ? quote_string (temp) - : quote_escapes (temp); + { + temp = (*temp && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) + ? quote_string (temp) + : quote_escapes (temp); + rflags |= W_ARRAYIND; + if (indp) + *indp = ind; + } else if (atype == 1 && temp && QUOTED_NULL (temp) && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) rflags |= W_HASQUOTEDNULL; } @@ -5666,7 +5675,7 @@ parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, c char *temp, *t; WORD_DESC *w; - w = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND); + w = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND, 0); t = w->word; /* Have to dequote here if necessary */ if (t) @@ -5683,7 +5692,7 @@ parameter_brace_expand_indir (name, var_is_special, quoted, quoted_dollar_atp, c if (t == 0) return (WORD_DESC *)NULL; - w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0); + w = parameter_brace_expand_word (t, SPECIAL_VAR(t, 0), quoted, 0, 0); free (t); return w; @@ -6119,13 +6128,18 @@ verify_substring_values (v, value, substr, vtype, e1p, e2p) /* Return the type of variable specified by VARNAME (simple variable, positional param, or array variable). Also return the value specified by VARNAME (value of a variable or a reference to an array element). + QUOTED is the standard description of quoting state, using Q_* defines. + FLAGS is currently a set of flags to pass to array_value. If IND is + non-null and not INTMAX_MIN, and FLAGS includes AV_USEIND, IND is + passed to array_value so the array index is not computed again. If this returns VT_VARIABLE, the caller assumes that CTLESC and CTLNUL characters in the value are quoted with CTLESC and takes appropriate steps. For convenience, *VALP is set to the dequoted VALUE. */ static int -get_var_and_type (varname, value, quoted, varp, valp) +get_var_and_type (varname, value, ind, quoted, flags, varp, valp) char *varname, *value; - int quoted; + arrayind_t ind; + int quoted, flags; SHELL_VAR **varp; char **valp; { @@ -6134,6 +6148,7 @@ get_var_and_type (varname, value, quoted, varp, valp) #if defined (ARRAY_VARS) SHELL_VAR *v; #endif + arrayind_t lind; /* This sets vtype to VT_VARIABLE or VT_POSPARMS */ vtype = (varname[0] == '@' || varname[0] == '*') && varname[1] == '\0'; @@ -6145,6 +6160,9 @@ get_var_and_type (varname, value, quoted, varp, valp) if (valid_array_reference (varname)) { v = array_variable_part (varname, &temp, (int *)0); + /* If we want to signal array_value to use an already-computed index, + set LIND to that index */ + lind = (ind != INTMAX_MIN && (flags & AV_USEIND)) ? ind : 0; if (v && (array_p (v) || assoc_p (v))) { /* [ */ if (ALL_ELEMENT_SUB (temp[0]) && temp[1] == ']') @@ -6158,7 +6176,7 @@ get_var_and_type (varname, value, quoted, varp, valp) else { vtype = VT_ARRAYMEMBER; - *valp = array_value (varname, 1, (int *)NULL, (arrayind_t *)NULL); + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); } *varp = v; } @@ -6175,7 +6193,7 @@ get_var_and_type (varname, value, quoted, varp, valp) { vtype = VT_ARRAYMEMBER; *varp = v; - *valp = array_value (varname, 1, (int *)NULL, (arrayind_t *)NULL); + *valp = array_value (varname, Q_DOUBLE_QUOTES, flags, (int *)NULL, &lind); } } else if ((v = find_variable (varname)) && (invisible_p (v) == 0) && (assoc_p (v) || array_p (v))) @@ -6242,9 +6260,11 @@ mb_substring (string, s, e) VARNAME. If VARNAME is an array variable, use the array elements. */ static char * -parameter_brace_substring (varname, value, substr, quoted) - char *varname, *value, *substr; - int quoted; +parameter_brace_substring (varname, value, ind, substr, quoted, flags) + char *varname, *value; + int ind; + char *substr; + int quoted, flags; { intmax_t e1, e2; int vtype, r, starsub; @@ -6257,7 +6277,7 @@ parameter_brace_substring (varname, value, substr, quoted) oname = this_command_name; this_command_name = varname; - vtype = get_var_and_type (varname, value, quoted, &v, &val); + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); if (vtype == -1) { this_command_name = oname; @@ -6353,20 +6373,28 @@ pat_subst (string, pat, rep, mflags) char *ret, *s, *e, *str, *rstr, *mstr; int rsize, rptr, l, replen, mtype, rxpand, rslen, mlen; + if (string == 0) + return (savestring ("")); + mtype = mflags & MATCH_TYPEMASK; +#if 0 /* bash-4.2 ? */ rxpand = (rep && *rep) ? shouldexp_replacement (rep) : 0; +#else + rxpand = 0; +#endif /* Special cases: * 1. A null pattern with mtype == MATCH_BEG means to prefix STRING * with REP and return the result. * 2. A null pattern with mtype == MATCH_END means to append REP to * STRING and return the result. + * These don't understand or process `&' in the replacement string. */ if ((pat == 0 || *pat == 0) && (mtype == MATCH_BEG || mtype == MATCH_END)) { replen = STRLEN (rep); - l = strlen (string); + l = STRLEN (string); ret = (char *)xmalloc (replen + l + 2); if (replen == 0) strcpy (ret, string); @@ -6444,7 +6472,7 @@ pat_subst (string, pat, rep, mflags) } /* Now copy the unmatched portion of the input string */ - if (*str) + if (str && *str) { RESIZE_MALLOCED_BUFFER (ret, rptr, STRLEN(str) + 1, rsize, 64); strcpy (ret + rptr, str); @@ -6505,9 +6533,11 @@ pos_params_pat_subst (string, pat, rep, mflags) and the string to substitute. QUOTED is a flags word containing the type of quoting currently in effect. */ static char * -parameter_brace_patsub (varname, value, patsub, quoted) - char *varname, *value, *patsub; - int quoted; +parameter_brace_patsub (varname, value, ind, patsub, quoted, flags) + char *varname, *value; + int ind; + char *patsub; + int quoted, flags; { int vtype, mflags, starsub, delim; char *val, *temp, *pat, *rep, *p, *lpatsub, *tt; @@ -6518,7 +6548,7 @@ parameter_brace_patsub (varname, value, patsub, quoted) this_command_name = varname; - vtype = get_var_and_type (varname, value, quoted, &v, &val); + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); if (vtype == -1) return ((char *)NULL); @@ -6693,11 +6723,11 @@ pos_params_modcase (string, pat, modop, mflags) to perform. QUOTED is a flags word containing the type of quoting currently in effect. */ static char * -parameter_brace_casemod (varname, value, modspec, patspec, quoted) +parameter_brace_casemod (varname, value, ind, modspec, patspec, quoted, flags) char *varname, *value; - int modspec; + int ind, modspec; char *patspec; - int quoted; + int quoted, flags; { int vtype, starsub, modop, mflags, x; char *val, *temp, *pat, *p, *lpat, *tt; @@ -6708,7 +6738,7 @@ parameter_brace_casemod (varname, value, modspec, patspec, quoted) this_command_name = varname; - vtype = get_var_and_type (varname, value, quoted, &v, &val); + vtype = get_var_and_type (varname, value, ind, quoted, flags, &v, &val); if (vtype == -1) return ((char *)NULL); @@ -6859,6 +6889,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta WORD_DESC *tdesc, *ret; int t_index, sindex, c, tflag, modspec; intmax_t number; + arrayind_t ind; temp = temp1 = value = (char *)NULL; var_is_set = var_is_null = var_is_special = check_nullness = 0; @@ -6885,6 +6916,8 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta ret = 0; tflag = 0; + ind = INTMAX_MIN; + /* If the name really consists of a special variable, then make sure that we have the entire name. We don't allow indirect references to special variables except `#', `?', `@' and `*'. */ @@ -7091,7 +7124,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta if (want_indir) tdesc = parameter_brace_expand_indir (name + 1, var_is_special, quoted, quoted_dollar_atp, contains_dollar_at); else - tdesc = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND|(pflags&PF_NOSPLIT2)); + tdesc = parameter_brace_expand_word (name, var_is_special, quoted, PF_IGNUNBOUND|(pflags&PF_NOSPLIT2), &ind); if (tdesc) { @@ -7129,7 +7162,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta /* If this is a substring spec, process it and add the result. */ if (want_substring) { - temp1 = parameter_brace_substring (name, temp, value, quoted); + temp1 = parameter_brace_substring (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); FREE (name); FREE (value); FREE (temp); @@ -7147,7 +7180,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta } else if (want_patsub) { - temp1 = parameter_brace_patsub (name, temp, value, quoted); + temp1 = parameter_brace_patsub (name, temp, ind, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); FREE (name); FREE (value); FREE (temp); @@ -7168,7 +7201,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta #if defined (CASEMOD_EXPANSIONS) else if (want_casemod) { - temp1 = parameter_brace_casemod (name, temp, modspec, value, quoted); + temp1 = parameter_brace_casemod (name, temp, ind, modspec, value, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); FREE (name); FREE (value); FREE (temp); @@ -7217,7 +7250,7 @@ parameter_brace_expand (string, indexp, quoted, pflags, quoted_dollar_atp, conta FREE (value); break; } - temp1 = parameter_brace_remove_pattern (name, temp, value, c, quoted); + temp1 = parameter_brace_remove_pattern (name, temp, ind, value, c, quoted, (tflag & W_ARRAYIND) ? AV_USEIND : 0); free (temp); free (value); free (name); @@ -8281,10 +8314,12 @@ add_twochars: else temp = (char *)NULL; +#if 0 /* We do not want to add quoted nulls to strings that are only partially quoted; we can throw them away. */ if (temp == 0 && quoted_state == PARTIALLY_QUOTED) continue; +#endif add_quoted_string: @@ -277,6 +277,7 @@ extern char *cond_expand_word __P((WORD_DESC *, int)); #define SD_INVERT 0x02 /* look for chars NOT in passed set */ #define SD_NOQUOTEDELIM 0x04 /* don't let single or double quotes act as delimiters */ #define SD_NOSKIPCMD 0x08 /* don't skip over $(, <(, or >( command/process substitution */ +#define SD_EXTGLOB 0x10 /* skip over extended globbing patterns if appropriate */ extern int skip_to_delim __P((char *, int, char *, int)); @@ -1,6 +1,6 @@ /* subst.h -- Names of externally visible functions in subst.c. */ -/* Copyright (C) 1993-2009 Free Software Foundation, Inc. +/* Copyright (C) 1993-2010 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. diff --git a/tests/array.right b/tests/array.right index fdc0793e..94c64a74 100644 --- a/tests/array.right +++ b/tests/array.right @@ -312,3 +312,32 @@ argv[3] = <€> argv[1] = <~> argv[2] = <^?> argv[3] = <€> +Monday Tuesday Wednesday Thursday Friday Saturday Sunday +Monday +Monday +Tuesday +Monday +Monday +Tuesday +Monday +Tuesday +Wednesday +Monday +Tuesday +Wednesday +monday, monday, tuesday +wednesday, wednesday, thursday +monday, monday, tuesday +Wednesday, Wednesday, Thursday +nday +esday +dnesday +nday +esday +dnesday +onday +uesday +ednesday +onday +uesday +ednesday diff --git a/tests/array.tests b/tests/array.tests index 435ac159..3b6996d3 100644 --- a/tests/array.tests +++ b/tests/array.tests @@ -386,3 +386,5 @@ ${THIS_SH} ./array7.sub ${THIS_SH} ./array8.sub ${THIS_SH} ./array9.sub + +${THIS_SH} ./array10.sub diff --git a/tests/array.tests~ b/tests/array.tests~ new file mode 100644 index 00000000..435ac159 --- /dev/null +++ b/tests/array.tests~ @@ -0,0 +1,388 @@ +# this is needed so that the bad assignments (b[]=bcde, for example) do not +# cause fatal shell errors when in posix mode +set +o posix + +set +a +# The calls to egrep -v are to filter out builtin array variables that are +# automatically set and possibly contain values that vary. + +# first make sure we handle the basics +x=() +echo ${x[@]} +unset x + +# this should be an error +test=(first & second) +echo $? +unset test + +# make sure declare -a converts an existing variable to an array +unset a +a=abcde +declare -a a +echo ${a[0]} + +unset a +a=abcde +a[2]=bdef + +unset b +declare -a b[256] + +unset c[2] +unset c[*] + +a[1]= + +_ENV=/bin/true +x=${_ENV[(_$-=0)+(_=1)-_${-%%*i*}]} + +declare -r c[100] + +echo ${a[0]} ${a[4]} +echo ${a[@]} + +echo ${a[*]} + +# this should print out values, too +declare -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)' + +unset a[7] +echo ${a[*]} + +unset a[4] +echo ${a[*]} + +echo ${a} +echo "${a}" +echo $a + +unset a[0] +echo ${a} + +echo ${a[@]} + +a[5]="hello world" +echo ${a[5]} +echo ${#a[5]} + +echo ${#a[@]} + +a[4+5/2]="test expression" +declare a["7 + 8"]="test 2" +a[7 + 8]="test 2" +echo ${a[@]} + +readonly a[5] +readonly a +# these two lines should output `declare' commands +readonly -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)' +declare -ar | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)' +# this line should output `readonly' commands, even for arrays +set -o posix +readonly -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)' +set +o posix + +declare -a d='([1]="" [2]="bdef" [5]="hello world" "test")' +d[9]="ninth element" + +declare -a e[10]=test # this works in post-bash-2.05 versions +declare -a e[10]='(test)' + +pass=/etc/passwd +declare -a f='("${d[@]}")' +b=([0]=this [1]=is [2]=a [3]=test [4]="$PS1" [5]=$pass) + +echo ${b[@]:2:3} + +declare -pa | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)' + +a[3]="this is a test" + +b[]=bcde +b[*]=aaa +echo ${b[ ]} + +c[-2]=4 +echo ${c[-4]} + +d[7]=(abdedfegeee) + +d=([]=abcde [1]="test test" [*]=last [-65]=negative ) + +unset d[12] +unset e[*] + +declare -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)' + +ps1='hello' +unset ps1[2] +unset ${ps1[2]} + +declare +a ps1 +declare +a c + +# the prompt should not print when using a here doc +read -p "array test: " -a rv <<! +this is a test of read using arrays +! + +echo ${rv[0]} ${rv[4]} +echo ${rv[@]} + +# the variable should be converted to an array when `read -a' is done +vv=1 +read -a vv <<! +this is a test of arrays +! +echo ${vv[0]} ${vv[3]} +echo ${vv[@]} +unset vv + +declare -a | egrep -v '(BASH_VERSINFO|PIPESTATUS|GROUPS)' + +export rv +#set + +x[4]=bbb +x=abde +echo $x +echo ${x[0]} +echo ${x[4]} +echo efgh | ( read x[1] ; echo ${x[1]} ) +echo wxyz | ( declare -a x ; read x ; echo $x ; echo ${x[0]} ) + +# Make sure that arrays can be used to save the positional paramters verbatim +set -- a 'b c' d 'e f g' h + +ARGV=( [0]=$0 "$@" ) + +for z in "${ARGV[@]}" +do + echo "$z" +done + +echo "$0" +for z in "$@" +do + echo "$z" +done + +# do various pattern removal and length tests +XPATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:.:/sbin:/usr/sbin + +xpath=( $( IFS=: ; echo $XPATH ) ) + +echo ${xpath[@]} +echo ${xpath[@]##*/} +echo ${xpath[0]##*/} +echo ${xpath[@]%%[!/]*} +echo ${xpath[0]%%[!/]*} +recho ${xpath##*/} +recho ${xpath%%[!/]*} +recho ${xpath[5]##*/} +recho ${xpath[5]%%[!/]*} + +# let's try to make it a DOS-style path + +zecho "${xpath[@]/\//\\}" +zecho "${xpath[@]//\//\\}" +zecho "${xpath[@]//[\/]/\\}" + +# length of the first element of the array, since array without subscript +# is equivalent to referencing first element +echo ${#xpath} -- ${#xpath[0]} + +# number of elements in the array +nelem=${#xpath[@]} +echo ${#xpath[@]} -- $nelem + +# total length of all elements in the array, including space separators +xx="${xpath[*]}" +echo ${#xx} + +# total length of all elements in the array +xx=$( IFS='' ; echo "${xpath[*]}" ) +echo ${#xx} + +unset xpath[nelem-1] + +nelem=${#xpath[@]} +echo ${#xpath[@]} -- $nelem + +# arrays and things that look like index assignments +array=(42 [1]=14 [2]=44) + +array2=(grep [ 123 ] \*) + +echo ${array[@]} +echo "${array2[@]}" + +# arrays and implicit arithmetic evaluation +declare -i -a iarray + +iarray=( 2+4 1+6 7+2 ) +echo ${iarray[@]} + +iarray[4]=4+1 +echo ${iarray[@]} + +# make sure assignment using the compound assignment syntax removes all +# of the old elements from the array value +barray=(old1 old2 old3 old4 old5) +barray=(new1 new2 new3) +echo "length = ${#barray[@]}" +echo "value = ${barray[*]}" + +# make sure the array code behaves correctly with respect to unset variables +set -u +( echo ${#narray[4]} ) + +${THIS_SH} ./array1.sub +${THIS_SH} ./array2.sub + +# some old bugs and ksh93 compatibility tests +${THIS_SH} ./array3.sub + +# some compound assingment parsing problems that showed up in bash-3.1-release +${THIS_SH} ./array4.sub + +set +u +cd /tmp + +touch 1=bar +foo=([10]="bar") +echo ${foo[0]} +rm 1=bar + +cd $OLDPWD + +foo=(a b c d e f g) +echo ${foo[@]} + +# quoted reserved words are ok +foo=(\for \case \if \then \else) +echo ${foo[@]} + +# quoted metacharacters are ok +foo=( [1]='<>' [2]='<' [3]='>' [4]='!' ) +echo ${foo[@]} + +# numbers are just words when not in a redirection context +foo=( 12 14 16 18 20 ) +echo ${foo[@]} + +foo=( 4414758999202 ) +echo ${foo[@]} + +# this was a bug in all versions of bash 2.x up to and including bash-2.04 +declare -a ddd=(aaa +bbb) +echo ${ddd[@]} + +# errors until post-bash-2.05a; now reserved words are OK +foo=(a b c for case if then else) + +foo=(for case if then else) + +# errors +metas=( <> < > ! ) +metas=( [1]=<> [2]=< [3]=> [4]=! ) + +# various expansions that didn't really work right until post-bash-2.04 +foo='abc' +echo ${foo[0]} ${#foo[0]} +echo ${foo[1]} ${#foo[1]} +echo ${foo[@]} ${#foo[@]} +echo ${foo[*]} ${#foo[*]} + +foo='' +echo ${foo[0]} ${#foo[0]} +echo ${foo[1]} ${#foo[1]} +echo ${foo[@]} ${#foo[@]} +echo ${foo[*]} ${#foo[*]} + +# new expansions added after bash-2.05b +x[0]=zero +x[1]=one +x[4]=four +x[10]=ten + +recho ${!x[@]} +recho "${!x[@]}" +recho ${!x[*]} +recho "${!x[*]}" + +# sparse array tests for code fixed in bash-3.0 +unset av +av[1]='one' +av[2]='' + +av[3]=three +av[5]=five +av[7]=seven + +echo include null element -- expect one +echo ${av[@]:1:2} # what happens when we include a null element? +echo include unset element -- expect three five +echo ${av[@]:3:2} # what happens when we include an unset element? +echo start at unset element -- expect five seven +echo ${av[@]:4:2} # what happens when we start at an unset element? + +echo too many elements -- expect three five seven +echo ${av[@]:3:5} # how about too many elements? + +echo positive offset - expect five seven +echo ${av[@]:5:2} +echo negative offset to unset element - expect seven +echo ${av[@]: -2:2} + +echo positive offset 2 - expect seven +echo ${av[@]: 6:2} +echo negative offset 2 - expect seven +echo ${av[@]: -1:2} + +echo out-of-range offset +echo ${av[@]:12} + +# parsing problems and other inconsistencies not fixed until post bash-3.0 +unset x +declare -a x=(')' $$) +[ ${x[1]} -eq $$ ] || echo bad + +unset x +declare -a x=(a b c d e) +echo ${x[4]} + +z=([1]=one [4]=four [7]=seven [10]=ten) + +echo ${#z[@]} + +echo ${!z[@]} + +unset x +declare -a x=(a \'b c\') + +echo "${x[1]}" + +unset x +declare -a x=(a 'b c') + +echo "${x[1]}" + +unset x +declare -a x=($0) +[ "${x[@]}" = $0 ] || echo double expansion of \$0 +declare -a x=(\$0) +echo "${x[@]}" + +# tests for bash-3.1 problems +${THIS_SH} ./array5.sub + +# tests for post-bash-3.2 problems, most fixed in bash-3.2 patches +${THIS_SH} ./array6.sub +${THIS_SH} ./array7.sub + +${THIS_SH} ./array8.sub + +${THIS_SH} ./array9.sub diff --git a/tests/array10.sub b/tests/array10.sub new file mode 100644 index 00000000..6d5a94c9 --- /dev/null +++ b/tests/array10.sub @@ -0,0 +1,52 @@ +days=({Mon,Tues,Wednes,Thurs,Fri,Satur,Sun}day) +echo ${days[@]} + +typeset -i count + +count=0 +echo ${days[${count}]} +echo ${days[$((count++))]} +echo ${days[$((count++))]} + +count=0 +echo ${days[count]} +echo ${days[count++]} +echo ${days[count++]} + +count=0 +echo ${days[$((count++))]/foo/bar} +echo ${days[$((count++))]/foo/bar} +echo ${days[$((count++))]/foo/bar} + +count=0 +echo ${days[count++]/foo/bar} +echo ${days[count++]/foo/bar} +echo ${days[count++]/foo/bar} + +count=0 +echo "${days[${count}],,}, ${days[$((count++))],,}, ${days[$((count++))],,}" +echo "${days[${count}],,}, ${days[$((count++))],,}, ${days[$((count++))],,}" + +count=0 +echo "${days[${count}],,}, ${days[$((count++))],,}, ${days[$((count++))],,}" +echo "${days[${count}]/foo/bar}, ${days[$((count++))]/foo/bar}, ${days[$((count++))]/foo/bar}" + +count=0 +echo ${days[$((count++))]:2} +echo ${days[$((count++))]:2} +echo ${days[$((count++))]:2} + +count=0 +echo ${days[count++]:2} +echo ${days[count++]:2} +echo ${days[count++]:2} + +count=0 +echo ${days[$((count++))]#?} +echo ${days[$((count++))]#?} +echo ${days[$((count++))]#?} + +count=0 +echo ${days[count++]#?} +echo ${days[count++]#?} +echo ${days[count++]#?} diff --git a/tests/new-exp.right b/tests/new-exp.right index 35e40d16..ac0058f9 100644 --- a/tests/new-exp.right +++ b/tests/new-exp.right @@ -537,6 +537,28 @@ bar () { echo < <(cat x1) } +start;ing0;ing1;ing2;ing3;ing4;ing5;ing6;ing7;ing8;ing9;ing10;ing11;ing12;ing13;ing14;ing15;ing16;ing17;ing18;ing19;ing20;ing21;ing22;ing23;ing24;ing25;ing26;ing27;ing28;ing29;ing30;ing31;ing32;ing33;ing34;ing35;ing36;ing37;ing38;ing39;ing40;ing41;ing42;ing43;ing44;ing45;ing46;ing47;ing48;ing49;ing50;ing51;ing52;ing53;ing54;ing55;ing56;ing57;ing58;ing59;ing60;ing61;ing62;ing63;ing64;ing65;ing66;ing67;ing68;ing69;ing70;ing71;ing72;ing73;ing74;ing75;ing76;ing77;ing78;ing79;ing80;ing81;ing82;ing83;ing84;ing85;ing86;ing87;ing88;ing89;ing90;ing91;ing92;ing93;ing94;ing95;ing96;ing97;ing98;ing99;ing100;ing101;ing102;ing103;ing104;ing105;ing106;ing107;ing108;ing109;ing110;ing111;ing112;ing113;ing114;ing115;ing116;ing117;ing118;ing119;ing120;ing121;ing122;ing123;ing124;ing125;ing126;ing127;ing128;ing129;ing130;ing131;ing132;ing133;ing134;ing135;ing136;ing137;ing138;ing139;ing140;ing141;ing142;ing143;ing144;ing145;ing146;ing147;ing148;ing149;ing150;ing151;ing152;ing153;ing154;ing155;ing156;ing157;ing158;ing159;ing160;ing161;ing162;ing163;ing164;ing165;ing166;ing167;ing168;ing169;ing170;ing171;ing172;ing173;ing174;ing175;ing176;ing177;ing178;ing179;ing180;ing181;ing182;ing183;ing184;ing185;ing186;ing187;ing188;ing189;ing190;ing191;ing192;ing193;ing194;ing195;ing196;ing197;ing198;ing199;ing200;ing201;ing202;ing203;ing204;ing205;ing206;ing207;ing208;ing209;ing210;ing211;ing212;ing213;ing214;ing215;ing216;ing217;ing218;ing219;ing220;ing221;ing222;ing223;ing224;ing225;ing226;ing227;ing228;ing229;ing230;ing231;ing232;ing233;ing234;ing235;ing236;ing237;ing238;ing239;ing240;ing241;ing242;ing243;ing244;ing245;ing246;ing247;ing248;ing249;ing250;ing251;ing252;ing253;ing254;ing255;ing256;ing257;ing258;ing259;ing260;ing261;ing262;ing263;ing264;ing265;ing266;ing267;ing268;ing269;ing270;ing271;ing272;ing273;ing274;ing275;ing276;ing277;ing278;ing279;ing280;ing281;ing282;ing283;ing284;ing285;ing286;ing287;ing288;ing289;ing290;ing291;ing292;ing293;ing294;ing295;ing296;ing297;ing298;ing299;ing300;ing301;ing302;ing303;ing304;ing305;ing306;ing307;ing308;ing309;ing310;ing311;ing312;ing313;ing314;ing315;ing316;ing317;ing318;ing319;ing320;ing321;ing322;ing323;ing324;ing325;ing326;ing327;ing328;ing329;ing330;ing331;ing332;ing333;ing334;ing335;ing336;ing337;ing338;ing339;ing340;ing341;ing342;ing343;ing344;ing345;ing346;ing347;ing348;ing349;ing350;ing351;ing352;ing353;ing354;ing355;ing356;ing357;ing358;ing359;ing360;ing361;ing362;ing363;ing364;ing365;ing366;ing367;ing368;ing369;ing370;ing371;ing372;ing373;ing374;ing375;ing376;ing377;ing378;ing379;ing380;ing381;ing382;ing383;ing384;ing385;ing386;ing387;ing388;ing389;ing390;ing391;ing392;ing393;ing394;ing395;ing396;ing397;ing398;ing399;ing400;ing401;ing402;ing403;ing404;ing405;ing406;ing407;ing408;ing409;ing410;ing411;ing412;ing413;ing414;ing415;ing416;ing417;ing418;ing419;ing420;ing421;ing422;ing423;ing424;ing425;ing426;ing427;ing428;ing429;ing430;ing431;ing432;ing433;ing434;ing435;ing436;ing437;ing438;ing439;ing440;ing441;ing442;ing443;ing444;ing445;ing446;ing447;ing448;ing449;ing450;ing451;ing452;ing453;ing454;ing455;ing456;ing457;ing458;ing459;ing460;ing461;ing462;ing463;ing464;ing465;ing466;ing467;ing468;ing469;ing470;ing471;ing472;ing473;ing474;ing475;ing476;ing477;ing478;ing479;ing480;ing481;ing482;ing483;ing484;ing485;ing486;ing487;ing488;ing489;ing490;ing491;ing492;ing493;ing494;ing495;ing496;ing497;ing498;ing499;ing500;ing501;ing502;ing503;ing504;ing505;ing506;ing507;ing508;ing509;ing510;ing511;ing512;ing513;ing514;ing515;ing516;ing517;ing518;ing519;ing520;ing521;ing522;ing523;ing524;ing525;ing526;ing527;ing528;ing529;ing530;ing531;ing532;ing533;ing534;ing535;ing536;ing537;ing538;ing539;ing540;ing541;ing542;ing543;ing544;ing545;ing546;ing547;ing548;ing549;ing550;ing551;ing552;ing553;ing554;ing555;ing556;ing557;ing558;ing559;ing560;ing561;ing562;ing563;ing564;ing565;ing566;ing567;ing568;ing569;ing570;ing571;ing572;ing573;ing574;ing575;ing576;ing577;ing578;ing579;ing580;ing581;ing582;ing583;ing584;ing585;ing586;ing587;ing588;ing589;ing590;ing591;ing592;ing593;ing594;ing595;ing596;ing597;ing598;ing599;ing600;ing601;ing602;ing603;ing604;ing605;ing606;ing607;ing608;ing609;ing610;ing611;ing612;ing613;ing614;ing615;ing616;ing617;ing618;ing619;ing620;ing621;ing622;ing623;ing624;ing625;ing626;ing627;ing628;ing629;ing630;ing631;ing632;ing633;ing634;ing635;ing636;ing637;ing638;ing639;ing640;ing641;ing642;ing643;ing644;ing645;ing646;ing647;ing648;ing649;ing650;ing651;ing652;ing653;ing654;ing655;ing656;ing657;ing658;ing659;ing660;ing661;ing662;ing663;ing664;ing665;ing666;ing667;ing668;ing669;ing670;ing671;ing672;ing673;ing674;ing675;ing676;ing677;ing678;ing679;ing680;ing681;ing682;ing683;ing684;ing685;ing686;ing687;ing688;ing689;ing690;ing691;ing692;ing693;ing694;ing695;ing696;ing697;ing698;ing699;ing700;ing701;ing702;ing703;ing704;ing705;ing706;ing707;ing708;ing709;ing710;ing711;ing712;ing713;ing714;ing715;ing716;ing717;ing718;ing719;ing720;ing721;ing722;ing723;ing724;ing725;ing726;ing727;ing728;ing729;ing730;ing731;ing732;ing733;ing734;ing735;ing736;ing737;ing738;ing739;ing740;ing741;ing742;ing743;ing744;ing745;ing746;ing747;ing748;ing749;ing750;ing751;ing752;ing753;ing754;ing755;ing756;ing757;ing758;ing759;ing760;ing761;ing762;ing763;ing764;ing765;ing766;ing767;ing768;ing769;ing770;ing771;ing772;ing773;ing774;ing775;ing776;ing777;ing778;ing779;ing780;ing781;ing782;ing783;ing784;ing785;ing786;ing787;ing788;ing789;ing790;ing791;ing792;ing793;ing794;ing795;ing796;ing797;ing798;ing799;ing800;ing801;ing802;ing803;ing804;ing805;ing806;ing807;ing808;ing809;ing810;ing811;ing812;ing813;ing814;ing815;ing816;ing817;ing818;ing819;ing820;ing821;ing822;ing823;ing824;ing825;ing826;ing827;ing828;ing829;ing830;ing831;ing832;ing833;ing834;ing835;ing836;ing837;ing838;ing839;ing840;ing841;ing842;ing843;ing844;ing845;ing846;ing847;ing848;ing849;ing850;ing851;ing852;ing853;ing854;ing855;ing856;ing857;ing858;ing859;ing860;ing861;ing862;ing863;ing864;ing865;ing866;ing867;ing868;ing869;ing870;ing871;ing872;ing873;ing874;ing875;ing876;ing877;ing878;ing879;ing880;ing881;ing882;ing883;ing884;ing885;ing886;ing887;ing888;ing889;ing890;ing891;ing892;ing893;ing894;ing895;ing896;ing897;ing898;ing899;ing900;ing901;ing902;ing903;ing904;ing905;ing906;ing907;ing908;ing909;ing910;ing911;ing912;ing913;ing914;ing915;ing916;ing917;ing918;ing919;ing920;ing921;ing922;ing923;ing924;ing925;ing926;ing927;ing928;ing929;ing930;ing931;ing932;ing933;ing934;ing935;ing936;ing937;ing938;ing939;ing940;ing941;ing942;ing943;ing944;ing945;ing946;ing947;ing948;ing949;ing950;ing951;ing952;ing953;ing954;ing955;ing956;ing957;ing958;ing959;ing960;ing961;ing962;ing963;ing964;ing965;ing966;ing967;ing968;ing969;ing970;ing971;ing972;ing973;ing974;ing975;ing976;ing977;ing978;ing979;ing980;ing981;ing982;ing983;ing984;ing985;ing986;ing987;ing988;ing989;ing990;ing991;ing992;ing993;ing994;ing995;ing996;ing997;ing998;ing999 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +start;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string999 +start;ing0;ing1;ing2;ing3;ing4;ing5;ing6;ing7;ing8;ing9;ing10;ing11;ing12;ing13;ing14;ing15;ing16;ing17;ing18;ing19;ing20;ing21;ing22;ing23;ing24;ing25;ing26;ing27;ing28;ing29;ing30;ing31;ing32;ing33;ing34;ing35;ing36;ing37;ing38;ing39;ing40;ing41;ing42;ing43;ing44;ing45;ing46;ing47;ing48;ing49;ing50;ing51;ing52;ing53;ing54;ing55;ing56;ing57;ing58;ing59;ing60;ing61;ing62;ing63;ing64;ing65;ing66;ing67;ing68;ing69;ing70;ing71;ing72;ing73;ing74;ing75;ing76;ing77;ing78;ing79;ing80;ing81;ing82;ing83;ing84;ing85;ing86;ing87;ing88;ing89;ing90;ing91;ing92;ing93;ing94;ing95;ing96;ing97;ing98;ing99;ing100;ing101;ing102;ing103;ing104;ing105;ing106;ing107;ing108;ing109;ing110;ing111;ing112;ing113;ing114;ing115;ing116;ing117;ing118;ing119;ing120;ing121;ing122;ing123;ing124;ing125;ing126;ing127;ing128;ing129;ing130;ing131;ing132;ing133;ing134;ing135;ing136;ing137;ing138;ing139;ing140;ing141;ing142;ing143;ing144;ing145;ing146;ing147;ing148;ing149;ing150;ing151;ing152;ing153;ing154;ing155;ing156;ing157;ing158;ing159;ing160;ing161;ing162;ing163;ing164;ing165;ing166;ing167;ing168;ing169;ing170;ing171;ing172;ing173;ing174;ing175;ing176;ing177;ing178;ing179;ing180;ing181;ing182;ing183;ing184;ing185;ing186;ing187;ing188;ing189;ing190;ing191;ing192;ing193;ing194;ing195;ing196;ing197;ing198;ing199;ing200;ing201;ing202;ing203;ing204;ing205;ing206;ing207;ing208;ing209;ing210;ing211;ing212;ing213;ing214;ing215;ing216;ing217;ing218;ing219;ing220;ing221;ing222;ing223;ing224;ing225;ing226;ing227;ing228;ing229;ing230;ing231;ing232;ing233;ing234;ing235;ing236;ing237;ing238;ing239;ing240;ing241;ing242;ing243;ing244;ing245;ing246;ing247;ing248;ing249;ing250;ing251;ing252;ing253;ing254;ing255;ing256;ing257;ing258;ing259;ing260;ing261;ing262;ing263;ing264;ing265;ing266;ing267;ing268;ing269;ing270;ing271;ing272;ing273;ing274;ing275;ing276;ing277;ing278;ing279;ing280;ing281;ing282;ing283;ing284;ing285;ing286;ing287;ing288;ing289;ing290;ing291;ing292;ing293;ing294;ing295;ing296;ing297;ing298;ing299;ing300;ing301;ing302;ing303;ing304;ing305;ing306;ing307;ing308;ing309;ing310;ing311;ing312;ing313;ing314;ing315;ing316;ing317;ing318;ing319;ing320;ing321;ing322;ing323;ing324;ing325;ing326;ing327;ing328;ing329;ing330;ing331;ing332;ing333;ing334;ing335;ing336;ing337;ing338;ing339;ing340;ing341;ing342;ing343;ing344;ing345;ing346;ing347;ing348;ing349;ing350;ing351;ing352;ing353;ing354;ing355;ing356;ing357;ing358;ing359;ing360;ing361;ing362;ing363;ing364;ing365;ing366;ing367;ing368;ing369;ing370;ing371;ing372;ing373;ing374;ing375;ing376;ing377;ing378;ing379;ing380;ing381;ing382;ing383;ing384;ing385;ing386;ing387;ing388;ing389;ing390;ing391;ing392;ing393;ing394;ing395;ing396;ing397;ing398;ing399;ing400;ing401;ing402;ing403;ing404;ing405;ing406;ing407;ing408;ing409;ing410;ing411;ing412;ing413;ing414;ing415;ing416;ing417;ing418;ing419;ing420;ing421;ing422;ing423;ing424;ing425;ing426;ing427;ing428;ing429;ing430;ing431;ing432;ing433;ing434;ing435;ing436;ing437;ing438;ing439;ing440;ing441;ing442;ing443;ing444;ing445;ing446;ing447;ing448;ing449;ing450;ing451;ing452;ing453;ing454;ing455;ing456;ing457;ing458;ing459;ing460;ing461;ing462;ing463;ing464;ing465;ing466;ing467;ing468;ing469;ing470;ing471;ing472;ing473;ing474;ing475;ing476;ing477;ing478;ing479;ing480;ing481;ing482;ing483;ing484;ing485;ing486;ing487;ing488;ing489;ing490;ing491;ing492;ing493;ing494;ing495;ing496;ing497;ing498;ing499;ing500;ing501;ing502;ing503;ing504;ing505;ing506;ing507;ing508;ing509;ing510;ing511;ing512;ing513;ing514;ing515;ing516;ing517;ing518;ing519;ing520;ing521;ing522;ing523;ing524;ing525;ing526;ing527;ing528;ing529;ing530;ing531;ing532;ing533;ing534;ing535;ing536;ing537;ing538;ing539;ing540;ing541;ing542;ing543;ing544;ing545;ing546;ing547;ing548;ing549;ing550;ing551;ing552;ing553;ing554;ing555;ing556;ing557;ing558;ing559;ing560;ing561;ing562;ing563;ing564;ing565;ing566;ing567;ing568;ing569;ing570;ing571;ing572;ing573;ing574;ing575;ing576;ing577;ing578;ing579;ing580;ing581;ing582;ing583;ing584;ing585;ing586;ing587;ing588;ing589;ing590;ing591;ing592;ing593;ing594;ing595;ing596;ing597;ing598;ing599;ing600;ing601;ing602;ing603;ing604;ing605;ing606;ing607;ing608;ing609;ing610;ing611;ing612;ing613;ing614;ing615;ing616;ing617;ing618;ing619;ing620;ing621;ing622;ing623;ing624;ing625;ing626;ing627;ing628;ing629;ing630;ing631;ing632;ing633;ing634;ing635;ing636;ing637;ing638;ing639;ing640;ing641;ing642;ing643;ing644;ing645;ing646;ing647;ing648;ing649;ing650;ing651;ing652;ing653;ing654;ing655;ing656;ing657;ing658;ing659;ing660;ing661;ing662;ing663;ing664;ing665;ing666;ing667;ing668;ing669;ing670;ing671;ing672;ing673;ing674;ing675;ing676;ing677;ing678;ing679;ing680;ing681;ing682;ing683;ing684;ing685;ing686;ing687;ing688;ing689;ing690;ing691;ing692;ing693;ing694;ing695;ing696;ing697;ing698;ing699;ing700;ing701;ing702;ing703;ing704;ing705;ing706;ing707;ing708;ing709;ing710;ing711;ing712;ing713;ing714;ing715;ing716;ing717;ing718;ing719;ing720;ing721;ing722;ing723;ing724;ing725;ing726;ing727;ing728;ing729;ing730;ing731;ing732;ing733;ing734;ing735;ing736;ing737;ing738;ing739;ing740;ing741;ing742;ing743;ing744;ing745;ing746;ing747;ing748;ing749;ing750;ing751;ing752;ing753;ing754;ing755;ing756;ing757;ing758;ing759;ing760;ing761;ing762;ing763;ing764;ing765;ing766;ing767;ing768;ing769;ing770;ing771;ing772;ing773;ing774;ing775;ing776;ing777;ing778;ing779;ing780;ing781;ing782;ing783;ing784;ing785;ing786;ing787;ing788;ing789;ing790;ing791;ing792;ing793;ing794;ing795;ing796;ing797;ing798;ing799;ing800;ing801;ing802;ing803;ing804;ing805;ing806;ing807;ing808;ing809;ing810;ing811;ing812;ing813;ing814;ing815;ing816;ing817;ing818;ing819;ing820;ing821;ing822;ing823;ing824;ing825;ing826;ing827;ing828;ing829;ing830;ing831;ing832;ing833;ing834;ing835;ing836;ing837;ing838;ing839;ing840;ing841;ing842;ing843;ing844;ing845;ing846;ing847;ing848;ing849;ing850;ing851;ing852;ing853;ing854;ing855;ing856;ing857;ing858;ing859;ing860;ing861;ing862;ing863;ing864;ing865;ing866;ing867;ing868;ing869;ing870;ing871;ing872;ing873;ing874;ing875;ing876;ing877;ing878;ing879;ing880;ing881;ing882;ing883;ing884;ing885;ing886;ing887;ing888;ing889;ing890;ing891;ing892;ing893;ing894;ing895;ing896;ing897;ing898;ing899;ing900;ing901;ing902;ing903;ing904;ing905;ing906;ing907;ing908;ing909;ing910;ing911;ing912;ing913;ing914;ing915;ing916;ing917;ing918;ing919;ing920;ing921;ing922;ing923;ing924;ing925;ing926;ing927;ing928;ing929;ing930;ing931;ing932;ing933;ing934;ing935;ing936;ing937;ing938;ing939;ing940;ing941;ing942;ing943;ing944;ing945;ing946;ing947;ing948;ing949;ing950;ing951;ing952;ing953;ing954;ing955;ing956;ing957;ing958;ing959;ing960;ing961;ing962;ing963;ing964;ing965;ing966;ing967;ing968;ing969;ing970;ing971;ing972;ing973;ing974;ing975;ing976;ing977;ing978;ing979;ing980;ing981;ing982;ing983;ing984;ing985;ing986;ing987;ing988;ing989;ing990;ing991;ing992;ing993;ing994;ing995;ing996;ing997;ing998;ing999 +start;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string999 +start;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string999 +ing999 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +art;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string999 +start;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string9 +start;ing0;ing1;ing2;ing3;ing4;ing5;ing6;ing7;ing8;ing9;ing10;ing11;ing12;ing13;ing14;ing15;ing16;ing17;ing18;ing19;ing20;ing21;ing22;ing23;ing24;ing25;ing26;ing27;ing28;ing29;ing30;ing31;ing32;ing33;ing34;ing35;ing36;ing37;ing38;ing39;ing40;ing41;ing42;ing43;ing44;ing45;ing46;ing47;ing48;ing49;ing50;ing51;ing52;ing53;ing54;ing55;ing56;ing57;ing58;ing59;ing60;ing61;ing62;ing63;ing64;ing65;ing66;ing67;ing68;ing69;ing70;ing71;ing72;ing73;ing74;ing75;ing76;ing77;ing78;ing79;ing80;ing81;ing82;ing83;ing84;ing85;ing86;ing87;ing88;ing89;ing90;ing91;ing92;ing93;ing94;ing95;ing96;ing97;ing98;ing99;ing100;ing101;ing102;ing103;ing104;ing105;ing106;ing107;ing108;ing109;ing110;ing111;ing112;ing113;ing114;ing115;ing116;ing117;ing118;ing119;ing120;ing121;ing122;ing123;ing124;ing125;ing126;ing127;ing128;ing129;ing130;ing131;ing132;ing133;ing134;ing135;ing136;ing137;ing138;ing139;ing140;ing141;ing142;ing143;ing144;ing145;ing146;ing147;ing148;ing149;ing150;ing151;ing152;ing153;ing154;ing155;ing156;ing157;ing158;ing159;ing160;ing161;ing162;ing163;ing164;ing165;ing166;ing167;ing168;ing169;ing170;ing171;ing172;ing173;ing174;ing175;ing176;ing177;ing178;ing179;ing180;ing181;ing182;ing183;ing184;ing185;ing186;ing187;ing188;ing189;ing190;ing191;ing192;ing193;ing194;ing195;ing196;ing197;ing198;ing199;ing200;ing201;ing202;ing203;ing204;ing205;ing206;ing207;ing208;ing209;ing210;ing211;ing212;ing213;ing214;ing215;ing216;ing217;ing218;ing219;ing220;ing221;ing222;ing223;ing224;ing225;ing226;ing227;ing228;ing229;ing230;ing231;ing232;ing233;ing234;ing235;ing236;ing237;ing238;ing239;ing240;ing241;ing242;ing243;ing244;ing245;ing246;ing247;ing248;ing249;ing250;ing251;ing252;ing253;ing254;ing255;ing256;ing257;ing258;ing259;ing260;ing261;ing262;ing263;ing264;ing265;ing266;ing267;ing268;ing269;ing270;ing271;ing272;ing273;ing274;ing275;ing276;ing277;ing278;ing279;ing280;ing281;ing282;ing283;ing284;ing285;ing286;ing287;ing288;ing289;ing290;ing291;ing292;ing293;ing294;ing295;ing296;ing297;ing298;ing299;ing300;ing301;ing302;ing303;ing304;ing305;ing306;ing307;ing308;ing309;ing310;ing311;ing312;ing313;ing314;ing315;ing316;ing317;ing318;ing319;ing320;ing321;ing322;ing323;ing324;ing325;ing326;ing327;ing328;ing329;ing330;ing331;ing332;ing333;ing334;ing335;ing336;ing337;ing338;ing339;ing340;ing341;ing342;ing343;ing344;ing345;ing346;ing347;ing348;ing349;ing350;ing351;ing352;ing353;ing354;ing355;ing356;ing357;ing358;ing359;ing360;ing361;ing362;ing363;ing364;ing365;ing366;ing367;ing368;ing369;ing370;ing371;ing372;ing373;ing374;ing375;ing376;ing377;ing378;ing379;ing380;ing381;ing382;ing383;ing384;ing385;ing386;ing387;ing388;ing389;ing390;ing391;ing392;ing393;ing394;ing395;ing396;ing397;ing398;ing399;ing400;ing401;ing402;ing403;ing404;ing405;ing406;ing407;ing408;ing409;ing410;ing411;ing412;ing413;ing414;ing415;ing416;ing417;ing418;ing419;ing420;ing421;ing422;ing423;ing424;ing425;ing426;ing427;ing428;ing429;ing430;ing431;ing432;ing433;ing434;ing435;ing436;ing437;ing438;ing439;ing440;ing441;ing442;ing443;ing444;ing445;ing446;ing447;ing448;ing449;ing450;ing451;ing452;ing453;ing454;ing455;ing456;ing457;ing458;ing459;ing460;ing461;ing462;ing463;ing464;ing465;ing466;ing467;ing468;ing469;ing470;ing471;ing472;ing473;ing474;ing475;ing476;ing477;ing478;ing479;ing480;ing481;ing482;ing483;ing484;ing485;ing486;ing487;ing488;ing489;ing490;ing491;ing492;ing493;ing494;ing495;ing496;ing497;ing498;ing499;ing500;ing501;ing502;ing503;ing504;ing505;ing506;ing507;ing508;ing509;ing510;ing511;ing512;ing513;ing514;ing515;ing516;ing517;ing518;ing519;ing520;ing521;ing522;ing523;ing524;ing525;ing526;ing527;ing528;ing529;ing530;ing531;ing532;ing533;ing534;ing535;ing536;ing537;ing538;ing539;ing540;ing541;ing542;ing543;ing544;ing545;ing546;ing547;ing548;ing549;ing550;ing551;ing552;ing553;ing554;ing555;ing556;ing557;ing558;ing559;ing560;ing561;ing562;ing563;ing564;ing565;ing566;ing567;ing568;ing569;ing570;ing571;ing572;ing573;ing574;ing575;ing576;ing577;ing578;ing579;ing580;ing581;ing582;ing583;ing584;ing585;ing586;ing587;ing588;ing589;ing590;ing591;ing592;ing593;ing594;ing595;ing596;ing597;ing598;ing599;ing600;ing601;ing602;ing603;ing604;ing605;ing606;ing607;ing608;ing609;ing610;ing611;ing612;ing613;ing614;ing615;ing616;ing617;ing618;ing619;ing620;ing621;ing622;ing623;ing624;ing625;ing626;ing627;ing628;ing629;ing630;ing631;ing632;ing633;ing634;ing635;ing636;ing637;ing638;ing639;ing640;ing641;ing642;ing643;ing644;ing645;ing646;ing647;ing648;ing649;ing650;ing651;ing652;ing653;ing654;ing655;ing656;ing657;ing658;ing659;ing660;ing661;ing662;ing663;ing664;ing665;ing666;ing667;ing668;ing669;ing670;ing671;ing672;ing673;ing674;ing675;ing676;ing677;ing678;ing679;ing680;ing681;ing682;ing683;ing684;ing685;ing686;ing687;ing688;ing689;ing690;ing691;ing692;ing693;ing694;ing695;ing696;ing697;ing698;ing699;ing700;ing701;ing702;ing703;ing704;ing705;ing706;ing707;ing708;ing709;ing710;ing711;ing712;ing713;ing714;ing715;ing716;ing717;ing718;ing719;ing720;ing721;ing722;ing723;ing724;ing725;ing726;ing727;ing728;ing729;ing730;ing731;ing732;ing733;ing734;ing735;ing736;ing737;ing738;ing739;ing740;ing741;ing742;ing743;ing744;ing745;ing746;ing747;ing748;ing749;ing750;ing751;ing752;ing753;ing754;ing755;ing756;ing757;ing758;ing759;ing760;ing761;ing762;ing763;ing764;ing765;ing766;ing767;ing768;ing769;ing770;ing771;ing772;ing773;ing774;ing775;ing776;ing777;ing778;ing779;ing780;ing781;ing782;ing783;ing784;ing785;ing786;ing787;ing788;ing789;ing790;ing791;ing792;ing793;ing794;ing795;ing796;ing797;ing798;ing799;ing800;ing801;ing802;ing803;ing804;ing805;ing806;ing807;ing808;ing809;ing810;ing811;ing812;ing813;ing814;ing815;ing816;ing817;ing818;ing819;ing820;ing821;ing822;ing823;ing824;ing825;ing826;ing827;ing828;ing829;ing830;ing831;ing832;ing833;ing834;ing835;ing836;ing837;ing838;ing839;ing840;ing841;ing842;ing843;ing844;ing845;ing846;ing847;ing848;ing849;ing850;ing851;ing852;ing853;ing854;ing855;ing856;ing857;ing858;ing859;ing860;ing861;ing862;ing863;ing864;ing865;ing866;ing867;ing868;ing869;ing870;ing871;ing872;ing873;ing874;ing875;ing876;ing877;ing878;ing879;ing880;ing881;ing882;ing883;ing884;ing885;ing886;ing887;ing888;ing889;ing890;ing891;ing892;ing893;ing894;ing895;ing896;ing897;ing898;ing899;ing900;ing901;ing902;ing903;ing904;ing905;ing906;ing907;ing908;ing909;ing910;ing911;ing912;ing913;ing914;ing915;ing916;ing917;ing918;ing919;ing920;ing921;ing922;ing923;ing924;ing925;ing926;ing927;ing928;ing929;ing930;ing931;ing932;ing933;ing934;ing935;ing936;ing937;ing938;ing939;ing940;ing941;ing942;ing943;ing944;ing945;ing946;ing947;ing948;ing949;ing950;ing951;ing952;ing953;ing954;ing955;ing956;ing957;ing958;ing959;ing960;ing961;ing962;ing963;ing964;ing965;ing966;ing967;ing968;ing969;ing970;ing971;ing972;ing973;ing974;ing975;ing976;ing977;ing978;ing979;ing980;ing981;ing982;ing983;ing984;ing985;ing986;ing987;ing988;ing989;ing990;ing991;ing992;ing993;ing994;ing995;ing996;ing997;ing998;ing999 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +start;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string999 +start;ing0;ing1;ing2;ing3;ing4;ing5;ing6;ing7;ing8;ing9;ing10;ing11;ing12;ing13;ing14;ing15;ing16;ing17;ing18;ing19;ing20;ing21;ing22;ing23;ing24;ing25;ing26;ing27;ing28;ing29;ing30;ing31;ing32;ing33;ing34;ing35;ing36;ing37;ing38;ing39;ing40;ing41;ing42;ing43;ing44;ing45;ing46;ing47;ing48;ing49;ing50;ing51;ing52;ing53;ing54;ing55;ing56;ing57;ing58;ing59;ing60;ing61;ing62;ing63;ing64;ing65;ing66;ing67;ing68;ing69;ing70;ing71;ing72;ing73;ing74;ing75;ing76;ing77;ing78;ing79;ing80;ing81;ing82;ing83;ing84;ing85;ing86;ing87;ing88;ing89;ing90;ing91;ing92;ing93;ing94;ing95;ing96;ing97;ing98;ing99;ing100;ing101;ing102;ing103;ing104;ing105;ing106;ing107;ing108;ing109;ing110;ing111;ing112;ing113;ing114;ing115;ing116;ing117;ing118;ing119;ing120;ing121;ing122;ing123;ing124;ing125;ing126;ing127;ing128;ing129;ing130;ing131;ing132;ing133;ing134;ing135;ing136;ing137;ing138;ing139;ing140;ing141;ing142;ing143;ing144;ing145;ing146;ing147;ing148;ing149;ing150;ing151;ing152;ing153;ing154;ing155;ing156;ing157;ing158;ing159;ing160;ing161;ing162;ing163;ing164;ing165;ing166;ing167;ing168;ing169;ing170;ing171;ing172;ing173;ing174;ing175;ing176;ing177;ing178;ing179;ing180;ing181;ing182;ing183;ing184;ing185;ing186;ing187;ing188;ing189;ing190;ing191;ing192;ing193;ing194;ing195;ing196;ing197;ing198;ing199;ing200;ing201;ing202;ing203;ing204;ing205;ing206;ing207;ing208;ing209;ing210;ing211;ing212;ing213;ing214;ing215;ing216;ing217;ing218;ing219;ing220;ing221;ing222;ing223;ing224;ing225;ing226;ing227;ing228;ing229;ing230;ing231;ing232;ing233;ing234;ing235;ing236;ing237;ing238;ing239;ing240;ing241;ing242;ing243;ing244;ing245;ing246;ing247;ing248;ing249;ing250;ing251;ing252;ing253;ing254;ing255;ing256;ing257;ing258;ing259;ing260;ing261;ing262;ing263;ing264;ing265;ing266;ing267;ing268;ing269;ing270;ing271;ing272;ing273;ing274;ing275;ing276;ing277;ing278;ing279;ing280;ing281;ing282;ing283;ing284;ing285;ing286;ing287;ing288;ing289;ing290;ing291;ing292;ing293;ing294;ing295;ing296;ing297;ing298;ing299;ing300;ing301;ing302;ing303;ing304;ing305;ing306;ing307;ing308;ing309;ing310;ing311;ing312;ing313;ing314;ing315;ing316;ing317;ing318;ing319;ing320;ing321;ing322;ing323;ing324;ing325;ing326;ing327;ing328;ing329;ing330;ing331;ing332;ing333;ing334;ing335;ing336;ing337;ing338;ing339;ing340;ing341;ing342;ing343;ing344;ing345;ing346;ing347;ing348;ing349;ing350;ing351;ing352;ing353;ing354;ing355;ing356;ing357;ing358;ing359;ing360;ing361;ing362;ing363;ing364;ing365;ing366;ing367;ing368;ing369;ing370;ing371;ing372;ing373;ing374;ing375;ing376;ing377;ing378;ing379;ing380;ing381;ing382;ing383;ing384;ing385;ing386;ing387;ing388;ing389;ing390;ing391;ing392;ing393;ing394;ing395;ing396;ing397;ing398;ing399;ing400;ing401;ing402;ing403;ing404;ing405;ing406;ing407;ing408;ing409;ing410;ing411;ing412;ing413;ing414;ing415;ing416;ing417;ing418;ing419;ing420;ing421;ing422;ing423;ing424;ing425;ing426;ing427;ing428;ing429;ing430;ing431;ing432;ing433;ing434;ing435;ing436;ing437;ing438;ing439;ing440;ing441;ing442;ing443;ing444;ing445;ing446;ing447;ing448;ing449;ing450;ing451;ing452;ing453;ing454;ing455;ing456;ing457;ing458;ing459;ing460;ing461;ing462;ing463;ing464;ing465;ing466;ing467;ing468;ing469;ing470;ing471;ing472;ing473;ing474;ing475;ing476;ing477;ing478;ing479;ing480;ing481;ing482;ing483;ing484;ing485;ing486;ing487;ing488;ing489;ing490;ing491;ing492;ing493;ing494;ing495;ing496;ing497;ing498;ing499;ing500;ing501;ing502;ing503;ing504;ing505;ing506;ing507;ing508;ing509;ing510;ing511;ing512;ing513;ing514;ing515;ing516;ing517;ing518;ing519;ing520;ing521;ing522;ing523;ing524;ing525;ing526;ing527;ing528;ing529;ing530;ing531;ing532;ing533;ing534;ing535;ing536;ing537;ing538;ing539;ing540;ing541;ing542;ing543;ing544;ing545;ing546;ing547;ing548;ing549;ing550;ing551;ing552;ing553;ing554;ing555;ing556;ing557;ing558;ing559;ing560;ing561;ing562;ing563;ing564;ing565;ing566;ing567;ing568;ing569;ing570;ing571;ing572;ing573;ing574;ing575;ing576;ing577;ing578;ing579;ing580;ing581;ing582;ing583;ing584;ing585;ing586;ing587;ing588;ing589;ing590;ing591;ing592;ing593;ing594;ing595;ing596;ing597;ing598;ing599;ing600;ing601;ing602;ing603;ing604;ing605;ing606;ing607;ing608;ing609;ing610;ing611;ing612;ing613;ing614;ing615;ing616;ing617;ing618;ing619;ing620;ing621;ing622;ing623;ing624;ing625;ing626;ing627;ing628;ing629;ing630;ing631;ing632;ing633;ing634;ing635;ing636;ing637;ing638;ing639;ing640;ing641;ing642;ing643;ing644;ing645;ing646;ing647;ing648;ing649;ing650;ing651;ing652;ing653;ing654;ing655;ing656;ing657;ing658;ing659;ing660;ing661;ing662;ing663;ing664;ing665;ing666;ing667;ing668;ing669;ing670;ing671;ing672;ing673;ing674;ing675;ing676;ing677;ing678;ing679;ing680;ing681;ing682;ing683;ing684;ing685;ing686;ing687;ing688;ing689;ing690;ing691;ing692;ing693;ing694;ing695;ing696;ing697;ing698;ing699;ing700;ing701;ing702;ing703;ing704;ing705;ing706;ing707;ing708;ing709;ing710;ing711;ing712;ing713;ing714;ing715;ing716;ing717;ing718;ing719;ing720;ing721;ing722;ing723;ing724;ing725;ing726;ing727;ing728;ing729;ing730;ing731;ing732;ing733;ing734;ing735;ing736;ing737;ing738;ing739;ing740;ing741;ing742;ing743;ing744;ing745;ing746;ing747;ing748;ing749;ing750;ing751;ing752;ing753;ing754;ing755;ing756;ing757;ing758;ing759;ing760;ing761;ing762;ing763;ing764;ing765;ing766;ing767;ing768;ing769;ing770;ing771;ing772;ing773;ing774;ing775;ing776;ing777;ing778;ing779;ing780;ing781;ing782;ing783;ing784;ing785;ing786;ing787;ing788;ing789;ing790;ing791;ing792;ing793;ing794;ing795;ing796;ing797;ing798;ing799;ing800;ing801;ing802;ing803;ing804;ing805;ing806;ing807;ing808;ing809;ing810;ing811;ing812;ing813;ing814;ing815;ing816;ing817;ing818;ing819;ing820;ing821;ing822;ing823;ing824;ing825;ing826;ing827;ing828;ing829;ing830;ing831;ing832;ing833;ing834;ing835;ing836;ing837;ing838;ing839;ing840;ing841;ing842;ing843;ing844;ing845;ing846;ing847;ing848;ing849;ing850;ing851;ing852;ing853;ing854;ing855;ing856;ing857;ing858;ing859;ing860;ing861;ing862;ing863;ing864;ing865;ing866;ing867;ing868;ing869;ing870;ing871;ing872;ing873;ing874;ing875;ing876;ing877;ing878;ing879;ing880;ing881;ing882;ing883;ing884;ing885;ing886;ing887;ing888;ing889;ing890;ing891;ing892;ing893;ing894;ing895;ing896;ing897;ing898;ing899;ing900;ing901;ing902;ing903;ing904;ing905;ing906;ing907;ing908;ing909;ing910;ing911;ing912;ing913;ing914;ing915;ing916;ing917;ing918;ing919;ing920;ing921;ing922;ing923;ing924;ing925;ing926;ing927;ing928;ing929;ing930;ing931;ing932;ing933;ing934;ing935;ing936;ing937;ing938;ing939;ing940;ing941;ing942;ing943;ing944;ing945;ing946;ing947;ing948;ing949;ing950;ing951;ing952;ing953;ing954;ing955;ing956;ing957;ing958;ing959;ing960;ing961;ing962;ing963;ing964;ing965;ing966;ing967;ing968;ing969;ing970;ing971;ing972;ing973;ing974;ing975;ing976;ing977;ing978;ing979;ing980;ing981;ing982;ing983;ing984;ing985;ing986;ing987;ing988;ing989;ing990;ing991;ing992;ing993;ing994;ing995;ing996;ing997;ing998;ing999 +start;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string999 +start;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string999 +ing999 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +art;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string999 +start;string0;string1;string2;string3;string4;string5;string6;string7;string8;string9;string10;string11;string12;string13;string14;string15;string16;string17;string18;string19;string20;string21;string22;string23;string24;string25;string26;string27;string28;string29;string30;string31;string32;string33;string34;string35;string36;string37;string38;string39;string40;string41;string42;string43;string44;string45;string46;string47;string48;string49;string50;string51;string52;string53;string54;string55;string56;string57;string58;string59;string60;string61;string62;string63;string64;string65;string66;string67;string68;string69;string70;string71;string72;string73;string74;string75;string76;string77;string78;string79;string80;string81;string82;string83;string84;string85;string86;string87;string88;string89;string90;string91;string92;string93;string94;string95;string96;string97;string98;string99;string100;string101;string102;string103;string104;string105;string106;string107;string108;string109;string110;string111;string112;string113;string114;string115;string116;string117;string118;string119;string120;string121;string122;string123;string124;string125;string126;string127;string128;string129;string130;string131;string132;string133;string134;string135;string136;string137;string138;string139;string140;string141;string142;string143;string144;string145;string146;string147;string148;string149;string150;string151;string152;string153;string154;string155;string156;string157;string158;string159;string160;string161;string162;string163;string164;string165;string166;string167;string168;string169;string170;string171;string172;string173;string174;string175;string176;string177;string178;string179;string180;string181;string182;string183;string184;string185;string186;string187;string188;string189;string190;string191;string192;string193;string194;string195;string196;string197;string198;string199;string200;string201;string202;string203;string204;string205;string206;string207;string208;string209;string210;string211;string212;string213;string214;string215;string216;string217;string218;string219;string220;string221;string222;string223;string224;string225;string226;string227;string228;string229;string230;string231;string232;string233;string234;string235;string236;string237;string238;string239;string240;string241;string242;string243;string244;string245;string246;string247;string248;string249;string250;string251;string252;string253;string254;string255;string256;string257;string258;string259;string260;string261;string262;string263;string264;string265;string266;string267;string268;string269;string270;string271;string272;string273;string274;string275;string276;string277;string278;string279;string280;string281;string282;string283;string284;string285;string286;string287;string288;string289;string290;string291;string292;string293;string294;string295;string296;string297;string298;string299;string300;string301;string302;string303;string304;string305;string306;string307;string308;string309;string310;string311;string312;string313;string314;string315;string316;string317;string318;string319;string320;string321;string322;string323;string324;string325;string326;string327;string328;string329;string330;string331;string332;string333;string334;string335;string336;string337;string338;string339;string340;string341;string342;string343;string344;string345;string346;string347;string348;string349;string350;string351;string352;string353;string354;string355;string356;string357;string358;string359;string360;string361;string362;string363;string364;string365;string366;string367;string368;string369;string370;string371;string372;string373;string374;string375;string376;string377;string378;string379;string380;string381;string382;string383;string384;string385;string386;string387;string388;string389;string390;string391;string392;string393;string394;string395;string396;string397;string398;string399;string400;string401;string402;string403;string404;string405;string406;string407;string408;string409;string410;string411;string412;string413;string414;string415;string416;string417;string418;string419;string420;string421;string422;string423;string424;string425;string426;string427;string428;string429;string430;string431;string432;string433;string434;string435;string436;string437;string438;string439;string440;string441;string442;string443;string444;string445;string446;string447;string448;string449;string450;string451;string452;string453;string454;string455;string456;string457;string458;string459;string460;string461;string462;string463;string464;string465;string466;string467;string468;string469;string470;string471;string472;string473;string474;string475;string476;string477;string478;string479;string480;string481;string482;string483;string484;string485;string486;string487;string488;string489;string490;string491;string492;string493;string494;string495;string496;string497;string498;string499;string500;string501;string502;string503;string504;string505;string506;string507;string508;string509;string510;string511;string512;string513;string514;string515;string516;string517;string518;string519;string520;string521;string522;string523;string524;string525;string526;string527;string528;string529;string530;string531;string532;string533;string534;string535;string536;string537;string538;string539;string540;string541;string542;string543;string544;string545;string546;string547;string548;string549;string550;string551;string552;string553;string554;string555;string556;string557;string558;string559;string560;string561;string562;string563;string564;string565;string566;string567;string568;string569;string570;string571;string572;string573;string574;string575;string576;string577;string578;string579;string580;string581;string582;string583;string584;string585;string586;string587;string588;string589;string590;string591;string592;string593;string594;string595;string596;string597;string598;string599;string600;string601;string602;string603;string604;string605;string606;string607;string608;string609;string610;string611;string612;string613;string614;string615;string616;string617;string618;string619;string620;string621;string622;string623;string624;string625;string626;string627;string628;string629;string630;string631;string632;string633;string634;string635;string636;string637;string638;string639;string640;string641;string642;string643;string644;string645;string646;string647;string648;string649;string650;string651;string652;string653;string654;string655;string656;string657;string658;string659;string660;string661;string662;string663;string664;string665;string666;string667;string668;string669;string670;string671;string672;string673;string674;string675;string676;string677;string678;string679;string680;string681;string682;string683;string684;string685;string686;string687;string688;string689;string690;string691;string692;string693;string694;string695;string696;string697;string698;string699;string700;string701;string702;string703;string704;string705;string706;string707;string708;string709;string710;string711;string712;string713;string714;string715;string716;string717;string718;string719;string720;string721;string722;string723;string724;string725;string726;string727;string728;string729;string730;string731;string732;string733;string734;string735;string736;string737;string738;string739;string740;string741;string742;string743;string744;string745;string746;string747;string748;string749;string750;string751;string752;string753;string754;string755;string756;string757;string758;string759;string760;string761;string762;string763;string764;string765;string766;string767;string768;string769;string770;string771;string772;string773;string774;string775;string776;string777;string778;string779;string780;string781;string782;string783;string784;string785;string786;string787;string788;string789;string790;string791;string792;string793;string794;string795;string796;string797;string798;string799;string800;string801;string802;string803;string804;string805;string806;string807;string808;string809;string810;string811;string812;string813;string814;string815;string816;string817;string818;string819;string820;string821;string822;string823;string824;string825;string826;string827;string828;string829;string830;string831;string832;string833;string834;string835;string836;string837;string838;string839;string840;string841;string842;string843;string844;string845;string846;string847;string848;string849;string850;string851;string852;string853;string854;string855;string856;string857;string858;string859;string860;string861;string862;string863;string864;string865;string866;string867;string868;string869;string870;string871;string872;string873;string874;string875;string876;string877;string878;string879;string880;string881;string882;string883;string884;string885;string886;string887;string888;string889;string890;string891;string892;string893;string894;string895;string896;string897;string898;string899;string900;string901;string902;string903;string904;string905;string906;string907;string908;string909;string910;string911;string912;string913;string914;string915;string916;string917;string918;string919;string920;string921;string922;string923;string924;string925;string926;string927;string928;string929;string930;string931;string932;string933;string934;string935;string936;string937;string938;string939;string940;string941;string942;string943;string944;string945;string946;string947;string948;string949;string950;string951;string952;string953;string954;string955;string956;string957;string958;string959;string960;string961;string962;string963;string964;string965;string966;string967;string968;string969;string970;string971;string972;string973;string974;string975;string976;string977;string978;string979;string980;string981;string982;string983;string984;string985;string986;string987;string988;string989;string990;string991;string992;string993;string994;string995;string996;string997;string998;string9 argv[1] = </> argv[1] = </> -./new-exp.tests: line 578: ABXD: parameter unset +./new-exp.tests: line 580: ABXD: parameter unset diff --git a/tests/new-exp.tests b/tests/new-exp.tests index 6c593642..28170419 100644 --- a/tests/new-exp.tests +++ b/tests/new-exp.tests @@ -567,6 +567,8 @@ ${THIS_SH} ./new-exp6.sub ${THIS_SH} ./new-exp7.sub +${THIS_SH} ./new-exp8.sub + # problems with stray CTLNUL in bash-4.0-alpha unset a a=/a diff --git a/tests/new-exp.tests~ b/tests/new-exp.tests~ index cd730190..6c593642 100644 --- a/tests/new-exp.tests~ +++ b/tests/new-exp.tests~ @@ -418,8 +418,9 @@ c=${var:4} expect nothing recho $c # as of bash-4.2, negative LENGTH means offset from the end -expect '<a>' c=${var:0:-2} +expect '<a>' +recho $c var=abcdefghi c=${var:3:12} diff --git a/tests/new-exp8.sub b/tests/new-exp8.sub new file mode 100644 index 00000000..6cae6aba --- /dev/null +++ b/tests/new-exp8.sub @@ -0,0 +1,95 @@ +pat1='str' +pat2='[^;]' +pat3='[[:alnum:]_]' +pat4='[[:alnum:]][[fu]b' +pat5='?tr' +pat6='?tr\' +pat7='[[:alnum:]]_' +pat8='*tr' + +declare z="start" +declare NUM=1000 + +#---------------------------------- +# create a long string with ';' +#---------------------------------- +for ((i=0; i<$NUM; i++)); do + z="$z;string$i" +done + +#z="$z;string;foo" + +#------------------------------ +# delete everything except ';' +#------------------------------ + +# try different patterns here +x="${z//$pat1}" +echo $x +x="${z//$pat2}" +echo $x +x="${z//$pat3}" +echo $x +x="${z//$pat4}" +echo $x +x="${z//$pat5}" +echo $x +x="${z//$pat6}" +echo $x +x="${z//$pat7}" +echo $x +x="${z//$pat8}" +echo $x + + +declare z="start" +declare NUM=1000 + +#---------------------------------- +# create a long string with ';' +#---------------------------------- +for ((i=0; i<$NUM; i++)); do + z="$z;string$i" +done + +#z="$z;string;foo" + +#------------------------------ +# delete everything except ';' +#------------------------------ + +# try different patterns here +x="${z//[^;]}" +echo $x +x="${z/#[^;][^;]}" +echo $x +x="${z/%[^;][^;]}" +echo $x + +export LANG=C LC_ALL=C LC_CTYPE=C + +# try different patterns here +x="${z//$pat1}" +echo $x +x="${z//$pat2}" +echo $x +x="${z//$pat3}" +echo $x +x="${z//$pat4}" +echo $x +x="${z//$pat5}" +echo $x +x="${z//$pat6}" +echo $x +x="${z//$pat7}" +echo $x +x="${z//$pat8}" +echo $x + +# try different patterns here +x="${z//[^;]}" +echo $x +x="${z/#[^;][^;]}" +echo $x +x="${z/%[^;][^;]}" +echo $x diff --git a/variables.c b/variables.c index 30f35ffa..78fd9da2 100644 --- a/variables.c +++ b/variables.c @@ -387,11 +387,14 @@ initialize_shell_variables (env, privmode) #endif { temp_var = bind_variable (name, string, 0); - if (legal_identifier (name)) - VSETATTR (temp_var, (att_exported | att_imported)); - else - VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); - array_needs_making = 1; + if (temp_var) + { + if (legal_identifier (name)) + VSETATTR (temp_var, (att_exported | att_imported)); + else + VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); + array_needs_making = 1; + } } name[char_index] = '='; @@ -2389,7 +2392,7 @@ bind_int_variable (lhs, rhs) #endif v = bind_variable (lhs, rhs, 0); - if (isint) + if (v && isint) VSETATTR (v, att_integer); return (v); @@ -2838,7 +2841,7 @@ delete_all_variables (hashed_vars) if (!entry) \ { \ entry = bind_variable (name, "", 0); \ - if (!no_invisible_vars) entry->attributes |= att_invisible; \ + if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \ } \ } \ while (0) |