/subst.c
C | 9071 lines | 7503 code | 704 blank | 864 comment | 1375 complexity | 27d13ea50af0f630524507c4f96fee2d MD5 | raw file
Possible License(s): GPL-3.0
Large files files are truncated, but you can click here to view the full file
- /* 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-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>
- #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 "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 *, char *, 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));
- 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 *, int, SHELL_VAR **, char **));
- static char *mb_substring __P((char *, int, int));
- static char *parameter_brace_substring __P((char *, char *, char *, int));
- static char *pos_params_pat_subst __P((char *, char *, char *, int));
- static char *parameter_brace_patsub __P((char *, char *, char *, int));
- static char *pos_params_casemod __P((char *, char *, int, int));
- static char *parameter_brace_casemod __P((char *, char *, int, char *, 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)
- {
- /* 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, 1, 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, 1, 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;
- }
- #if 0
- /* Process a nested command substitution, but only if we're parsing a
- command substitution. XXX - for bash-4.2 */
- if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LPAREN)
- {
- si = i + 2;
- t = extract_command_subst (string, &si, flags);
- i = si + 1;
- continue;
- }
- #endif
- /* 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;
- char *result, *t;
- DECLARE_MBSTATE;
- pass_character = 0;
- nesting_level = 1;
- slen = strlen (string + *sindex) + *sindex;
- 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;
- }
- /* 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;
- }
- /* 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"), "}", 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
- …
Large files files are truncated, but you can click here to view the full file