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