This file is type.def, from which is created type.c.
It implements the builtin "type" in Bash.

Copyright (C) 1987-2002 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 2, 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; see the file COPYING.  If not, write to the Free Software
Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.

$PRODUCES type.c

$BUILTIN type
$FUNCTION type_builtin
$SHORT_DOC type [-afptP] name [name ...]
For each NAME, indicate how it would be interpreted if used as a
command name.

If the -t option is used, `type' outputs a single word which is one of
`alias', `keyword', `function', `builtin', `file' or `', if NAME is an
alias, shell reserved word, shell function, shell builtin, disk file,
or unfound, respectively.

If the -p flag is used, `type' either returns the name of the disk
file that would be executed, or nothing if `type -t NAME' would not
return `file'.

If the -a flag is used, `type' displays all of the places that contain
an executable named `file'.  This includes aliases, builtins, and
functions, if and only if the -p flag is not also used.

The -f flag suppresses shell function lookup.

The -P flag forces a PATH search for each NAME, even if it is an alias,
builtin, or function, and returns the name of the disk file that would
be executed.
$END

#include <config.h>

#include "../bashtypes.h"
#include "posixstat.h"

#if defined (HAVE_UNISTD_H)
#  include <unistd.h>
#endif

#include <stdio.h>
#include "../bashansi.h"

#include "../shell.h"
#include "../findcmd.h"
#include "../hashcmd.h"

#if defined (ALIAS)
#include "../alias.h"
#endif /* ALIAS */

#include "common.h"
#include "bashgetopt.h"

extern int find_reserved_word __P((char *));

extern char *this_command_name;

/* For each word in LIST, find out what the shell is going to do with
   it as a simple command. i.e., which file would this shell use to
   execve, or if it is a builtin command, or an alias.  Possible flag
   arguments:
	-t		Returns the "type" of the object, one of
			`alias', `keyword', `function', `builtin',
			or `file'.

	-p		Returns the pathname of the file if -type is
			a file.

	-a		Returns all occurrences of words, whether they
			be a filename in the path, alias, function,
			or builtin.

	-f		Suppress shell function lookup, like `command'.

	-P		Force a path search even in the presence of other
			definitions.

   Order of evaluation:
	alias
	keyword
	function
	builtin
	file
 */

int
type_builtin (list)
     WORD_LIST *list;
{
  int dflags, successful_finds, opt;
  WORD_LIST *this;

  if (list == 0)
    return (EXECUTION_SUCCESS);

  dflags = CDESC_SHORTDESC;	/* default */
  successful_finds = 0;

  /* Handle the obsolescent `-type', `-path', and `-all' by prescanning
     the arguments and converting those options to the form that
     internal_getopt recognizes. Converts `--type', `--path', and `--all'
     also. THIS SHOULD REALLY GO AWAY. */
  for (this = list; this && this->word->word[0] == '-'; this = this->next)
    {
      char *flag = &(this->word->word[1]);

      if (STREQ (flag, "type") || STREQ (flag, "-type"))
	{
	  this->word->word[1] = 't';
	  this->word->word[2] = '\0';
	}
      else if (STREQ (flag, "path") || STREQ (flag, "-path"))
	{
	  this->word->word[1] = 'p';
	  this->word->word[2] = '\0';
	}
      else if (STREQ (flag, "all") || STREQ (flag, "-all"))
	{
	  this->word->word[1] = 'a';
	  this->word->word[2] = '\0';
	}
    }

  reset_internal_getopt ();
  while ((opt = internal_getopt (list, "afptP")) != -1)
    {
      switch (opt)
	{
	case 'a':
	  dflags |= CDESC_ALL;
	  break;
	case 'f':
	  dflags |= CDESC_NOFUNCS;
	  break;
	case 'p':
	  dflags |= CDESC_PATH_ONLY;
	  dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
	  break;
	case 't':
	  dflags |= CDESC_TYPE;
	  dflags &= ~(CDESC_PATH_ONLY|CDESC_SHORTDESC);
	  break;
	case 'P':	/* shorthand for type -ap */
	  dflags |= (CDESC_PATH_ONLY|CDESC_FORCE_PATH);
	  dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
	  break;
	default:
	  builtin_usage ();
	  return (EX_USAGE);
	}
    }
  list = loptend;

  while (list)
    {
      int found;

      found = describe_command (list->word->word, dflags);

      if (!found && (dflags & (CDESC_PATH_ONLY|CDESC_TYPE)) == 0)
	sh_notfound (list->word->word);

      successful_finds += found;
      list = list->next;
    }

  fflush (stdout);

  return ((successful_finds != 0) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
}

/*
 * Describe COMMAND as required by the type and command builtins.
 *
 * Behavior is controlled by DFLAGS.  Flag values are
 *	CDESC_ALL	print all descriptions of a command
 *	CDESC_SHORTDESC	print the description for type and command -V
 *	CDESC_REUSABLE	print in a format that may be reused as input
 *	CDESC_TYPE	print the type for type -t
 *	CDESC_PATH_ONLY	print the path for type -p
 *	CDESC_FORCE_PATH	force a path search for type -P
 *	CDESC_NOFUNCS	skip function lookup for type -f
 *
 * CDESC_ALL says whether or not to look for all occurrences of COMMAND, or
 * return after finding it once.
 */
int
describe_command (command, dflags)
     char *command;
     int dflags;
{
  int found, i, found_file, f, all;
  char *full_path, *x;
  SHELL_VAR *func;
#if defined (ALIAS)
  alias_t *alias;
#endif

  all = (dflags & CDESC_ALL) != 0;
  found = found_file = 0;
  full_path = (char *)NULL;

#if defined (ALIAS)
  /* Command is an alias? */
  if (((dflags & CDESC_FORCE_PATH) == 0) && (alias = find_alias (command)))
    {
      if (dflags & CDESC_TYPE)
	puts ("alias");
      else if (dflags & CDESC_SHORTDESC)
	printf ("%s is aliased to `%s'\n", command, alias->value);
      else if (dflags & CDESC_REUSABLE)
	{
	  x = sh_single_quote (alias->value);
	  printf ("alias %s=%s\n", command, x);
	  free (x);
	}

      found = 1;

      if (all == 0)
	return (1);
    }
#endif /* ALIAS */

  /* Command is a shell reserved word? */
  if (((dflags & CDESC_FORCE_PATH) == 0) && (i = find_reserved_word (command)) >= 0)
    {
      if (dflags & CDESC_TYPE)
	puts ("keyword");
      else if (dflags & CDESC_SHORTDESC)
	printf ("%s is a shell keyword\n", command);
      else if (dflags & CDESC_REUSABLE)
	printf ("%s\n", command);

      found = 1;

      if (all == 0)
	return (1);
    }

  /* Command is a function? */
  if (((dflags & (CDESC_FORCE_PATH|CDESC_NOFUNCS)) == 0) && (func = find_function (command)))
    {
      if (dflags & CDESC_TYPE)
	puts ("function");
      else if (dflags & CDESC_SHORTDESC)
	{
#define PRETTY_PRINT_FUNC 1
	  char *result;

	  printf ("%s is a function\n", command);

	  /* We're blowing away THE_PRINTED_COMMAND here... */

	  result = named_function_string (command,
					  (COMMAND *) function_cell (func),
					  PRETTY_PRINT_FUNC);
	  printf ("%s\n", result);
#undef PRETTY_PRINT_FUNC
	}
      else if (dflags & CDESC_REUSABLE)
	printf ("%s\n", command);

      found = 1;

      if (all == 0)
	return (1);
    }

  /* Command is a builtin? */
  if (((dflags & CDESC_FORCE_PATH) == 0) && find_shell_builtin (command))
    {
      if (dflags & CDESC_TYPE)
	puts ("builtin");
      else if (dflags & CDESC_SHORTDESC)
	printf ("%s is a shell builtin\n", command);
      else if (dflags & CDESC_REUSABLE)
	printf ("%s\n", command);

      found = 1;

      if (all == 0)
	return (1);
    }

  /* Command is a disk file? */
  /* If the command name given is already an absolute command, just
     check to see if it is executable. */
  if (absolute_program (command))
    {
      f = file_status (command);
      if (f & FS_EXECABLE)
	{
	  if (dflags & CDESC_TYPE)
	    puts ("file");
	  else if (dflags & CDESC_SHORTDESC)
	    printf ("%s is %s\n", command, command);
	  else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
	    printf ("%s\n", command);

	  /* There's no use looking in the hash table or in $PATH,
	     because they're not consulted when an absolute program
	     name is supplied. */
	  return (1);
	}
    }

  /* If the user isn't doing "-a", then we might care about
     whether the file is present in our hash table. */
  if (all == 0 || (dflags & CDESC_FORCE_PATH))
    {
      if (full_path = phash_search (command))
	{
	  if (dflags & CDESC_TYPE)
	    puts ("file");
	  else if (dflags & CDESC_SHORTDESC)
	    printf ("%s is hashed (%s)\n", command, full_path);
	  else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
	    printf ("%s\n", full_path);

	  free (full_path);
	  return (1);
	}
    }

  /* Now search through $PATH. */
  while (1)
    {
      if (all == 0)
	full_path = find_user_command (command);
      else
	full_path =
	  user_command_matches (command, FS_EXEC_ONLY, found_file);
	  /* XXX - should that be FS_EXEC_PREFERRED? */

      if (!full_path)
	break;

      /* If we found the command as itself by looking through $PATH, it
	 probably doesn't exist.  Check whether or not the command is an
	 executable file.  If it's not, don't report a match. */
      if (STREQ (full_path, command))
	{
	  f = file_status (full_path);
	  if ((f & FS_EXECABLE) == 0)
	    {
	      free (full_path);
	      full_path = (char *)NULL;
	      if (all == 0)
		break;
	    }
	  else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY|CDESC_SHORTDESC))
	    full_path = sh_makepath ((char *)NULL, full_path, MP_DOCWD);
	}

      found_file++;
      found = 1;

      if (dflags & CDESC_TYPE)
	puts ("file");
      else if (dflags & CDESC_SHORTDESC)
	printf ("%s is %s\n", command, full_path);
      else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
	printf ("%s\n", full_path);

      free (full_path);
      full_path = (char *)NULL;

      if (all == 0)
	break;
    }

  return (found);
}
