1This file is type.def, from which is created type.c.
2It implements the builtin "type" in Bash.
3
4Copyright (C) 1987-2002 Free Software Foundation, Inc.
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
8Bash is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13Bash is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with Bash; see the file COPYING.  If not, write to the Free Software
20Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA.
21
22$PRODUCES type.c
23
24$BUILTIN type
25$FUNCTION type_builtin
26$SHORT_DOC type [-afptP] name [name ...]
27For each NAME, indicate how it would be interpreted if used as a
28command name.
29
30If the -t option is used, `type' outputs a single word which is one of
31`alias', `keyword', `function', `builtin', `file' or `', if NAME is an
32alias, shell reserved word, shell function, shell builtin, disk file,
33or unfound, respectively.
34
35If the -p flag is used, `type' either returns the name of the disk
36file that would be executed, or nothing if `type -t NAME' would not
37return `file'.
38
39If the -a flag is used, `type' displays all of the places that contain
40an executable named `file'.  This includes aliases, builtins, and
41functions, if and only if the -p flag is not also used.
42
43The -f flag suppresses shell function lookup.
44
45The -P flag forces a PATH search for each NAME, even if it is an alias,
46builtin, or function, and returns the name of the disk file that would
47be executed.
48$END
49
50#include <config.h>
51
52#include "../bashtypes.h"
53#include "posixstat.h"
54
55#if defined (HAVE_UNISTD_H)
56#  include <unistd.h>
57#endif
58
59#include <stdio.h>
60#include "../bashansi.h"
61#include "../bashintl.h"
62
63#include "../shell.h"
64#include "../findcmd.h"
65#include "../hashcmd.h"
66
67#if defined (ALIAS)
68#include "../alias.h"
69#endif /* ALIAS */
70
71#include "common.h"
72#include "bashgetopt.h"
73
74extern int find_reserved_word __P((char *));
75
76extern char *this_command_name;
77extern int expand_aliases, posixly_correct;
78
79/* For each word in LIST, find out what the shell is going to do with
80   it as a simple command. i.e., which file would this shell use to
81   execve, or if it is a builtin command, or an alias.  Possible flag
82   arguments:
83	-t		Returns the "type" of the object, one of
84			`alias', `keyword', `function', `builtin',
85			or `file'.
86
87	-p		Returns the pathname of the file if -type is
88			a file.
89
90	-a		Returns all occurrences of words, whether they
91			be a filename in the path, alias, function,
92			or builtin.
93
94	-f		Suppress shell function lookup, like `command'.
95
96	-P		Force a path search even in the presence of other
97			definitions.
98
99   Order of evaluation:
100	alias
101	keyword
102	function
103	builtin
104	file
105 */
106
107int
108type_builtin (list)
109     WORD_LIST *list;
110{
111  int dflags, successful_finds, opt;
112  WORD_LIST *this;
113
114  if (list == 0)
115    return (EXECUTION_SUCCESS);
116
117  dflags = CDESC_SHORTDESC;	/* default */
118  successful_finds = 0;
119
120  /* Handle the obsolescent `-type', `-path', and `-all' by prescanning
121     the arguments and converting those options to the form that
122     internal_getopt recognizes. Converts `--type', `--path', and `--all'
123     also. THIS SHOULD REALLY GO AWAY. */
124  for (this = list; this && this->word->word[0] == '-'; this = this->next)
125    {
126      char *flag = &(this->word->word[1]);
127
128      if (STREQ (flag, "type") || STREQ (flag, "-type"))
129	{
130	  this->word->word[1] = 't';
131	  this->word->word[2] = '\0';
132	}
133      else if (STREQ (flag, "path") || STREQ (flag, "-path"))
134	{
135	  this->word->word[1] = 'p';
136	  this->word->word[2] = '\0';
137	}
138      else if (STREQ (flag, "all") || STREQ (flag, "-all"))
139	{
140	  this->word->word[1] = 'a';
141	  this->word->word[2] = '\0';
142	}
143    }
144
145  reset_internal_getopt ();
146  while ((opt = internal_getopt (list, "afptP")) != -1)
147    {
148      switch (opt)
149	{
150	case 'a':
151	  dflags |= CDESC_ALL;
152	  break;
153	case 'f':
154	  dflags |= CDESC_NOFUNCS;
155	  break;
156	case 'p':
157	  dflags |= CDESC_PATH_ONLY;
158	  dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
159	  break;
160	case 't':
161	  dflags |= CDESC_TYPE;
162	  dflags &= ~(CDESC_PATH_ONLY|CDESC_SHORTDESC);
163	  break;
164	case 'P':	/* shorthand for type -ap */
165	  dflags |= (CDESC_PATH_ONLY|CDESC_FORCE_PATH);
166	  dflags &= ~(CDESC_TYPE|CDESC_SHORTDESC);
167	  break;
168	default:
169	  builtin_usage ();
170	  return (EX_USAGE);
171	}
172    }
173  list = loptend;
174
175  while (list)
176    {
177      int found;
178
179      found = describe_command (list->word->word, dflags);
180
181      if (!found && (dflags & (CDESC_PATH_ONLY|CDESC_TYPE)) == 0)
182	sh_notfound (list->word->word);
183
184      successful_finds += found;
185      list = list->next;
186    }
187
188  fflush (stdout);
189
190  return ((successful_finds != 0) ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
191}
192
193/*
194 * Describe COMMAND as required by the type and command builtins.
195 *
196 * Behavior is controlled by DFLAGS.  Flag values are
197 *	CDESC_ALL	print all descriptions of a command
198 *	CDESC_SHORTDESC	print the description for type and command -V
199 *	CDESC_REUSABLE	print in a format that may be reused as input
200 *	CDESC_TYPE	print the type for type -t
201 *	CDESC_PATH_ONLY	print the path for type -p
202 *	CDESC_FORCE_PATH	force a path search for type -P
203 *	CDESC_NOFUNCS	skip function lookup for type -f
204 *	CDESC_ABSPATH	convert to absolute path, no ./ prefix
205 *
206 * CDESC_ALL says whether or not to look for all occurrences of COMMAND, or
207 * return after finding it once.
208 */
209int
210describe_command (command, dflags)
211     char *command;
212     int dflags;
213{
214  int found, i, found_file, f, all;
215  char *full_path, *x;
216  SHELL_VAR *func;
217#if defined (ALIAS)
218  alias_t *alias;
219#endif
220
221  all = (dflags & CDESC_ALL) != 0;
222  found = found_file = 0;
223  full_path = (char *)NULL;
224
225#if defined (ALIAS)
226  /* Command is an alias? */
227  if (((dflags & CDESC_FORCE_PATH) == 0) && expand_aliases && (alias = find_alias (command)))
228    {
229      if (dflags & CDESC_TYPE)
230	puts ("alias");
231      else if (dflags & CDESC_SHORTDESC)
232	printf (_("%s is aliased to `%s'\n"), command, alias->value);
233      else if (dflags & CDESC_REUSABLE)
234	{
235	  x = sh_single_quote (alias->value);
236	  printf ("alias %s=%s\n", command, x);
237	  free (x);
238	}
239
240      found = 1;
241
242      if (all == 0)
243	return (1);
244    }
245#endif /* ALIAS */
246
247  /* Command is a shell reserved word? */
248  if (((dflags & CDESC_FORCE_PATH) == 0) && (i = find_reserved_word (command)) >= 0)
249    {
250      if (dflags & CDESC_TYPE)
251	puts ("keyword");
252      else if (dflags & CDESC_SHORTDESC)
253	printf (_("%s is a shell keyword\n"), command);
254      else if (dflags & CDESC_REUSABLE)
255	printf ("%s\n", command);
256
257      found = 1;
258
259      if (all == 0)
260	return (1);
261    }
262
263  /* Command is a function? */
264  if (((dflags & (CDESC_FORCE_PATH|CDESC_NOFUNCS)) == 0) && (func = find_function (command)))
265    {
266      if (dflags & CDESC_TYPE)
267	puts ("function");
268      else if (dflags & CDESC_SHORTDESC)
269	{
270#define PRETTY_PRINT_FUNC 1
271	  char *result;
272
273	  printf (_("%s is a function\n"), command);
274
275	  /* We're blowing away THE_PRINTED_COMMAND here... */
276
277	  result = named_function_string (command,
278					  (COMMAND *) function_cell (func),
279					  PRETTY_PRINT_FUNC);
280	  printf ("%s\n", result);
281#undef PRETTY_PRINT_FUNC
282	}
283      else if (dflags & CDESC_REUSABLE)
284	printf ("%s\n", command);
285
286      found = 1;
287
288      if (all == 0)
289	return (1);
290    }
291
292  /* Command is a builtin? */
293  if (((dflags & CDESC_FORCE_PATH) == 0) && find_shell_builtin (command))
294    {
295      if (dflags & CDESC_TYPE)
296	puts ("builtin");
297      else if (dflags & CDESC_SHORTDESC)
298	printf (_("%s is a shell builtin\n"), command);
299      else if (dflags & CDESC_REUSABLE)
300	printf ("%s\n", command);
301
302      found = 1;
303
304      if (all == 0)
305	return (1);
306    }
307
308  /* Command is a disk file? */
309  /* If the command name given is already an absolute command, just
310     check to see if it is executable. */
311  if (absolute_program (command))
312    {
313      f = file_status (command);
314      if (f & FS_EXECABLE)
315	{
316	  if (dflags & CDESC_TYPE)
317	    puts ("file");
318	  else if (dflags & CDESC_SHORTDESC)
319	    printf (_("%s is %s\n"), command, command);
320	  else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
321	    printf ("%s\n", command);
322
323	  /* There's no use looking in the hash table or in $PATH,
324	     because they're not consulted when an absolute program
325	     name is supplied. */
326	  return (1);
327	}
328    }
329
330  /* If the user isn't doing "-a", then we might care about
331     whether the file is present in our hash table. */
332  if (all == 0 || (dflags & CDESC_FORCE_PATH))
333    {
334      if (full_path = phash_search (command))
335	{
336	  if (dflags & CDESC_TYPE)
337	    puts ("file");
338	  else if (dflags & CDESC_SHORTDESC)
339	    printf (_("%s is hashed (%s)\n"), command, full_path);
340	  else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
341	    printf ("%s\n", full_path);
342
343	  free (full_path);
344	  return (1);
345	}
346    }
347
348  /* Now search through $PATH. */
349  while (1)
350    {
351      if (all == 0)
352	full_path = find_user_command (command);
353      else
354	full_path =
355	  user_command_matches (command, FS_EXEC_ONLY, found_file);
356	  /* XXX - should that be FS_EXEC_PREFERRED? */
357
358      if (!full_path)
359	break;
360
361      /* If we found the command as itself by looking through $PATH, it
362	 probably doesn't exist.  Check whether or not the command is an
363	 executable file.  If it's not, don't report a match.  This is
364	 the default posix mode behavior */
365      if (STREQ (full_path, command) || posixly_correct)
366	{
367	  f = file_status (full_path);
368	  if ((f & FS_EXECABLE) == 0)
369	    {
370	      free (full_path);
371	      full_path = (char *)NULL;
372	      if (all == 0)
373		break;
374	    }
375	  else if (ABSPATH (full_path))
376	    ;	/* placeholder; don't need to do anything yet */
377	  else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY|CDESC_SHORTDESC))
378	    {
379	      f = MP_DOCWD | ((dflags & CDESC_ABSPATH) ? MP_RMDOT : 0);
380	      full_path = sh_makepath ((char *)NULL, full_path, f);
381	    }
382	}
383      /* If we require a full path and don't have one, make one */
384      else if ((dflags & CDESC_ABSPATH) && ABSPATH (full_path) == 0)
385	full_path = sh_makepath ((char *)NULL, full_path, MP_DOCWD|MP_RMDOT);
386
387      found_file++;
388      found = 1;
389
390      if (dflags & CDESC_TYPE)
391	puts ("file");
392      else if (dflags & CDESC_SHORTDESC)
393	printf ("%s is %s\n", command, full_path);
394      else if (dflags & (CDESC_REUSABLE|CDESC_PATH_ONLY))
395	printf ("%s\n", full_path);
396
397      free (full_path);
398      full_path = (char *)NULL;
399
400      if (all == 0)
401	break;
402    }
403
404  return (found);
405}
406