static WORD_LIST *
expand_word_list_internal (list, eflags)
     WORD_LIST *list;
     int eflags;
{
  WORD_LIST *new_list, *temp_list;
  int tint;
  char *savecmd;

  tempenv_assign_error = 0;
  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)
		{
		  savecmd = this_command_name;
		  this_command_name = (char *)NULL;	/* no arithmetic errors */
		  tint = do_word_assignment (temp_list->word, 0);
		  this_command_name = savecmd;
		  /* 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;
      int is_special_builtin, is_builtin_or_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;

      is_builtin_or_func = (new_list && new_list->word && (find_shell_builtin (new_list->word->word) || find_function (new_list->word->word)));
      /* Posix says that special builtins exit if a variable assignment error
	 occurs in an assignment preceding it. */
      is_special_builtin = (posixly_correct && new_list && new_list->word && find_special_builtin (new_list->word->word));
      
      for (temp_list = subst_assign_varlist; temp_list; temp_list = temp_list->next)
	{
	  savecmd = this_command_name;
	  this_command_name = (char *)NULL;
	  assigning_in_environment = (assign_func == assign_in_env);
	  tint = (*assign_func) (temp_list->word, is_builtin_or_func);
	  assigning_in_environment = 0;
	  this_command_name = savecmd;
	  /* 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 && is_special_builtin)
		    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;
    }

  return (new_list);